-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathnodejs_example.js
More file actions
138 lines (122 loc) · 4.13 KB
/
nodejs_example.js
File metadata and controls
138 lines (122 loc) · 4.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/**
* Standalone Node.js example using Express to handle OAuth 2.0 login with Google, storing user data in a SQLite database
*/
const express = require("express");
const axios = require("axios");
const sqlite3 = require("sqlite3").verbose();
const crypto = require("crypto");
const jwt = require("jsonwebtoken");
const jwksClient = require("jwks-rsa");
const app = express();
const db = new sqlite3.Database(":memory:");
// Initialize database
db.serialize(() => {
db.run(
"CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, email TEXT)"
);
db.run(
"CREATE TABLE federated_credentials (user_id INTEGER, provider TEXT, subject TEXT, PRIMARY KEY (provider, subject))"
);
});
// Configuration
const CLIENT_ID = process.env.GOOGLE_CLIENT_ID;
const CLIENT_SECRET = process.env.GOOGLE_CLIENT_SECRET;
const REDIRECT_URI = "https://example.com/oauth2/callback";
const SCOPE = "openid profile email";
// JWKS client to fetch Google's public keys
const jwks = jwksClient({
jwksUri: "https://www.googleapis.com/oauth2/v3/certs",
});
// Function to verify JWT
async function verifyIdToken(idToken) {
return new Promise((resolve, reject) => {
jwt.verify(
idToken,
(header, callback) => {
jwks.getSigningKey(header.kid, (err, key) => {
callback(null, key.getPublicKey());
});
},
{
audience: CLIENT_ID,
issuer: "https://accounts.google.com",
},
(err, decoded) => {
if (err) return reject(err);
resolve(decoded);
}
);
});
}
// Generate a random state for CSRF protection
app.get("/login", (req, res) => {
const state = crypto.randomBytes(16).toString("hex");
req.session.state = state; // Store state in session
const authUrl = `https://accounts.google.com/o/oauth2/auth?client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URI}&scope=${SCOPE}&response_type=code&state=${state}`;
res.redirect(authUrl);
});
// OAuth callback
app.get("/oauth2/callback", async (req, res) => {
const { code, state } = req.query;
// Verify state to prevent CSRF
if (state !== req.session.state) {
return res.status(403).send("Invalid state parameter");
}
try {
// Exchange code for tokens
const tokenResponse = await axios.post(
"https://oauth2.googleapis.com/token",
{
code,
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
redirect_uri: REDIRECT_URI,
grant_type: "authorization_code",
}
);
const { id_token } = tokenResponse.data;
// Verify ID token (JWT)
const decoded = await verifyIdToken(id_token);
const { sub: subject, name, email } = decoded;
// Check if user exists in federated_credentials
db.get(
"SELECT * FROM federated_credentials WHERE provider = ? AND subject = ?",
["https://accounts.google.com", subject],
(err, cred) => {
if (err) return res.status(500).send("Database error");
if (!cred) {
// New user: create account
db.run(
"INSERT INTO users (name, email) VALUES (?, ?)",
[name, email],
function (err) {
if (err) return res.status(500).send("Database error");
const userId = this.lastID;
db.run(
"INSERT INTO federated_credentials (user_id, provider, subject) VALUES (?, ?, ?)",
[userId, "https://accounts.google.com", subject],
(err) => {
if (err) return res.status(500).send("Database error");
res.send(`Logged in as ${name} (${email})`);
}
);
}
);
} else {
// Existing user: fetch and log in
db.get(
"SELECT * FROM users WHERE id = ?",
[cred.user_id],
(err, user) => {
if (err || !user) return res.status(500).send("Database error");
res.send(`Logged in as ${user.name} (${user.email})`);
}
);
}
}
);
} catch (error) {
res.status(500).send("OAuth or JWT verification error");
}
});
app.listen(3000, () => console.log("Server running on port 3000"));