process.env.DEBUG = 'app*'; const express = require('express'); const cookieParser = require('cookie-parser') const app = express(); const jwt = require('jsonwebtoken'); const Debug = require('debug'); const path = require('path'); const cors = require('cors'); const bodyParser = require('body-parser'); const favicon = require('serve-favicon'); const cert = require('./cert'); let issuer = 'localhost:3333'; let jwksOrigin = `https://${issuer}/`; const audience = process.env.AUDIENCE || 'https://generic-audience'; const debug = Debug('app'); let { privateKey, certDer, thumbprint, exponent, modulus } = cert(jwksOrigin); const sessions = {} const challenges = {} const corsOpts = (req, cb) => { cb(null, { origin: req.headers.origin }) } // Configure our small auth0-mock-server app.options('*', cors(corsOpts)) .use(cors()) .use(bodyParser.json()) .use(bodyParser.urlencoded({ extended: true })) .use(cookieParser()) .use(express.static(`${__dirname}/public`)) .use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); // This route can be used to generate a valid jwt-token. app.post('/oauth/token', (req, res) => { const code = req.body.code const session = sessions[code] let date = Math.floor(Date.now() / 1000); let accessToken = jwt.sign(Buffer.from(JSON.stringify({ iss: jwksOrigin, aud: [audience], sub: 'auth0|' + session.email, iat: date, exp: date + 7200, azp: session.clientId, })), privateKey, { algorithm: 'RS256', keyid: thumbprint }); let idToken = jwt.sign(Buffer.from(JSON.stringify({ iss: jwksOrigin, aud: session.clientId, nonce: session.nonce, sub: 'auth0|' + session.email, iat: date, exp: date + 7200, azp: session.clientId, name: 'Example Person', picture: 'https://cdn.playbuzz.com/cdn/5458360f-32ea-460e-a707-1a2d26760558/70bda687-cb84-4756-8a44-8cf735ed87b3.jpg', 'https://unbound.se/roles': session.roles })), privateKey, { algorithm: 'RS256', keyid: thumbprint }); debug('Signed token for ' + session.email); // res.json({ token }); res.json({ access_token: accessToken, id_token: idToken, scope: 'openid%20profile%20email', expires_in: 7200, token_type: 'Bearer' }) }); // This route can be used to generate a valid jwt-token. app.get('/token/:email', (req, res) => { if (!req.params.email) { debug('No user was given!'); return res.status(400).send('user is missing'); } const token = jwt.sign({ user_id: 'auth0|' + req.params.email, }, privateKey); debug('Signed token for ' + req.params.email); res.json({ token }); }); app.post('/code', (req, res) => { if (!req.body.email || !req.body.password || !req.body.codeChallenge) { debug('Body is invalid!', req.body); return res.status(400).send('Email or password is missing!'); } const code = req.body.codeChallenge challenges[req.body.codeChallenge] = code const state = req.body.state let roles = [] if (req.body.admin === 'true') { roles = ['admin'] } sessions[code] = { email: req.body.email, password: req.body.password, state: req.body.state, nonce: req.body.nonce, clientId: req.body.clientId, codeChallenge: req.body.codeChallenge, roles: roles } res.redirect(`${req.body.redirect}?domain=${issuer}&code=${code}&state=${encodeURIComponent(state)}`) }) app.get('/authorize', (req, res) => { const redirect = req.query.redirect_uri; const state = req.query.state; const nonce = req.query.nonce; const clientId = req.query.client_id; const codeChallenge = req.query.code_challenge; const prompt = req.query.prompt; const responseMode = req.query.response_mode; if (prompt === 'none' && responseMode === 'web_message') { const code = req.cookies['auth0'] const session = sessions[code] session.nonce = nonce session.state = state session.codeChallenge = codeChallenge res.send(` `) } else { res.cookie('auth0', codeChallenge, { sameSite: 'None', secure: true, httpOnly: true }) res.send(` Auth
Login
`) } }); app.get('/userinfo', (req, res) => { res.contentType('application/json').send(JSON.stringify({ picture: 'https://cdn.playbuzz.com/cdn/5458360f-32ea-460e-a707-1a2d26760558/70bda687-cb84-4756-8a44-8cf735ed87b3.jpg' })) }); app.get('/v2/logout', (req, res) => { res.redirect(`${req.query.returnTo}?domain=${issuer}`) }) app.get('/.well-known/jwks.json', (req, res) => { res .contentType('application/json') .send(JSON.stringify({ keys: [ { alg: 'RS256', // e: 'AQAB', e: exponent, kid: thumbprint, kty: 'RSA', n: modulus, use: 'sig', x5c: [certDer], x5t: thumbprint, }, ], })); }); // This route returns the inside of a jwt-token. Your main application // should use this route to keep the auth0-flow app.post('/tokeninfo', (req, res) => { if (!req.body.id_token) { debug('No token given in the body!'); return res.status(401).send('missing id_token'); } const data = jwt.decode(req.body.id_token); if (data) { debug('Return token data from ' + data.user_id); res.json(data); } else { debug('The token was invalid and could not be decoded!'); res.status(401).send('invalid id_token'); } }); app.post('/issuer', (req, res) => { if (!req.body.issuer) { debug('No issuer given in the body!'); return res.status(401).send('missing issuer'); } issuer = req.body.issuer; jwksOrigin = `https://${issuer}/`; const { privateKey: key, certDer: der, thumbPrint: thumb, exponent: exp, modulus: mod } = cert(jwksOrigin); privateKey = key; certDer = der; thumbprint = thumb; exponent = exp; modulus = mod; debug('Issuer set to ' + req.body.issuer); res.send('ok') }); app.listen(3333, () => { debug('Auth0-Mock-Server listening on port 3333!'); });