const express = require("express"); const crypto = require("crypto"); const querystring = require("querystring"); const jwt = require("jsonwebtoken"); const jwksClient = require("jwks-rsa"); const app = express(); const CLIENT_ID = "ec014b23-edde-4a09-9a41-36bff5630829"; // appID const CLIENT_SECRET = "Y.Q8Q~HuoEbCsICK18eG4oAtqjMe5eGyWSilLaZI"; // appid secret const TENANT = "publicanub.onmicrosoft.com"; const TENANT_ID = "23c95e59-28bd-472a-bbd4-4e310dd8f031"; // organisation id const REDIRECT_URI = "http://localhost:3000/callback"; const AUTH_URL = `https://login.microsoftonline.com/${TENANT}/oauth2/v2.0`; const SCOPES = "openid profile email"; // Fetch Microsoft's public key by kid to verify JWT signatures const keys = jwksClient({ jwksUri: "https://login.microsoftonline.com/common/discovery/v2.0/keys" }); function getKey(header, cb) { keys.getSigningKey(header.kid, (err, key) => cb(err, key?.getPublicKey())); } function verifyJwt(token) { return new Promise((resolve, reject) => { jwt.verify(token, getKey, { audience: CLIENT_ID, // Multi-tenant: accept tokens from any Azure AD tenant issuer: (iss) => iss.startsWith("https://login.microsoftonline.com/") && iss.endsWith("/v2.0"), algorithms: ["RS256"], }, (err, decoded) => err ? reject(err) : resolve(decoded)); }); } // POST to Microsoft's token endpoint function fetchTokens(code) { return new Promise((resolve, reject) => { const body = querystring.stringify({ client_id: CLIENT_ID, client_secret: CLIENT_SECRET, grant_type: "authorization_code", code, redirect_uri: REDIRECT_URI, scope: SCOPES, }); const req = require("https").request({ hostname: "login.microsoftonline.com", path: `/${TENANT}/oauth2/v2.0/token`, method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", "Content-Length": Buffer.byteLength(body) }, }, (res) => { let data = ""; res.on("data", (c) => data += c); res.on("end", () => resolve(JSON.parse(data))); }); req.on("error", reject); req.write(body); req.end(); }); } app.get("/", (_, res) => res.send('

OIDC Demo

Sign In with Azure AD')); app.get("/login", (_, res) => { const params = querystring.stringify({ client_id: CLIENT_ID, response_type: "code", redirect_uri: REDIRECT_URI, scope: SCOPES, state: crypto.randomBytes(16).toString("hex"), nonce: crypto.randomBytes(16).toString("hex"), response_mode: "query", }); res.redirect(`${AUTH_URL}/authorize?${params}`); }); // Microsoft redirects here with ?code=&state= // The code is a one-time-use temporary key that we exchange + client_secret for JWT tokens app.get("/callback", async (req, res) => { const { code, error, error_description } = req.query; if (error) return res.send(`
Error: ${error}: ${error_description}
`); try { const tokens = await fetchTokens(code); if (tokens.error) return res.send(`
Token error: ${JSON.stringify(tokens, null, 2)}
Home`); // Verify JWT signature against Microsoft's public key + check aud, iss, exp const v = await verifyJwt(tokens.id_token); // // Only allow specific tenants // const ALLOWED_TENANTS = ["23c95e59-28bd-472a-bbd4-4e310dd8f031"]; // if (!ALLOWED_TENANTS.includes(v.tid)) { // return res.status(403).send(`
Tenant not allowed: ${v.tid}
Home`); // } res.send(`

JWT Verified

Callback received (req.query)

${JSON.stringify(req.query, null, 2)}

This code was exchanged + client_secret for the tokens below

Checks

Signature: VALID (RS256, Microsoft public key)

Audience: ${v.aud} | Issuer: ${v.iss} (tenant: ${v.tid})

Expires: ${new Date(v.exp * 1000).toISOString()}

User

oid: ${v.oid} | ${v.name} | ${v.preferred_username}

Claims

${JSON.stringify(v, null, 2)}
Start over `); } catch (err) { res.status(401).send(`
JWT verification failed: ${err.message}
Try again`); } }); app.listen(3000, () => console.log("http://localhost:3000"));