Last active
August 16, 2021 11:07
-
-
Save svvac/a83f86912f361bb6e077dfcb913ac1cb to your computer and use it in GitHub Desktop.
openid-client test app
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const express = require('express'); | |
const openid = require('openid-client'); | |
const helpers = require('./helpers'); | |
const app1 = express(); | |
app1.get('/login', async (req, res) => { | |
try { | |
// Store authentication state in a secretbox cookie | |
const authstate = { | |
date: new Date(), | |
ip: req.ip, | |
ua: req.header('User-Agent'), | |
nonce: openid.generators.nonce(), | |
verifier: openid.generators.codeVerifier(), | |
}; | |
res.cookie('astk', helpers.createSecretToken(authstate), { httpOnly: true }); | |
// Create authorization url | |
const code_challenge = openid.generators.codeChallenge(authstate.verifier); | |
const client = await helpers.getClient(); | |
const authurl = client.authorizationUrl({ | |
scope: 'openid email profile', | |
nonce: authstate.nonce, | |
code_challenge, | |
code_challenge_method: 'S256', | |
}); | |
res.redirect(authurl); | |
} | |
catch (err) { | |
console.error(err); | |
res.sendStatus(500); | |
} | |
}); | |
app1.listen(3000); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const express = require('express'); | |
const helpers = require('./helpers'); | |
const { STATE_VALIDITY } = require('./constants'); | |
const app2 = express(); | |
app2.use(require('cookie-parser')()); | |
app2.get('/callback', async (req, res) => { | |
try { | |
let authstate; | |
try { | |
if (!req.cookies.astk) return res.redirect('http://localhost:3000/login'); | |
res.clearCookie('astk', { httpOnly: true }); | |
authstate = helpers.readSecretToken(req.cookies.astk); | |
// Verify state | |
if (new Date(authstate.date) <= new Date() - STATE_VALIDITY) throw new Error('state expired'); | |
if (authstate.ip !== req.ip) throw new Error('IP mismatch'); | |
if (authstate.ua !== req.header('User-Agent')) throw new Error('User Agent mismatch'); | |
} | |
catch (err) { | |
console.error('STATE ERROR', err); | |
res.sendStatus(400); | |
return; | |
} | |
try { | |
const client = await helpers.getClient(); // Instanciate a new client | |
const params = client.callbackParams(req); | |
const tokset = await client.callback('http://localhost:3001/callback', params, { | |
nonce: authstate.nonce, | |
code_verifier: authstate.verifier, | |
}); | |
res.status(200); | |
res.send(tokset.claims()); | |
} catch (err) { | |
console.error('CB ERROR', err); | |
res.sendStatus(400); | |
return; | |
} | |
} | |
catch (err) { | |
console.error(err); | |
res.sendStatus(500); | |
} | |
}); | |
app2.listen(3001); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module.exports = { | |
// A 32-byte secret key for secretbox | |
SECRET_KEY: Buffer.from('23566e086a5cbf92e4fcd38b6f3a151b25be69379792c108c6d5feaf3dcd202e', 'hex'), | |
STATE_VALIDITY: 60 * 10 * 1000, // Auth state is valid 10min | |
CLIENT_DISCOVERY_URL: 'https://accounts.google.com', | |
// Options used to instanciate a client | |
CLIENT_OPTIONS: { | |
client_id: 'oauth client id', | |
client_secret: 'oauth client secret', | |
redirect_uris: [ 'http://localhost:3001/callback' ], | |
response_types: [ 'code' ], | |
} | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const tnacl = require('tweetnacl'); | |
const openid = require('openid-client'); | |
const { SECRET_KEY, CLIENT_DISCOVERY_URL, CLIENT_OPTIONS } = require('./constants'); | |
module.exports = { | |
createSecretToken, | |
readSecretToken, | |
getClient, | |
} | |
function createSecretToken (data) { | |
const nonce = tnacl.randomBytes(tnacl.secretbox.nonceLength); | |
const payload = Buffer.from(JSON.stringify(data)); | |
const box = tnacl.secretbox(payload, nonce, SECRET_KEY); | |
const token = Buffer.concat([ nonce, box ]); | |
return token.toString('base64'); | |
} | |
function readSecretToken (token) { | |
const buf = Buffer.from(token, 'base64'); | |
const nonce = buf.slice(0, tnacl.secretbox.nonceLength); | |
const box = buf.slice(tnacl.secretbox.nonceLength); | |
const payload = Buffer.from(tnacl.secretbox.open(box, nonce, SECRET_KEY)); | |
const obj = JSON.parse(payload.toString()); | |
return obj; | |
} | |
async function getClient () { | |
const issuer = await openid.Issuer.discover(CLIENT_DISCOVERY_URL); | |
const client = new issuer.Client(CLIENT_OPTIONS); | |
return client; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment