Node.js openid-client
Openid-client is a server side OpenID Relying Party (RP, Client) implementation for Node.js runtime. In this documentation, example configuration implement OpenID Hybrid Flow.
Install
Node.js version >=12.0.0 is recommended, but ^10.19.0 lts/dubnium is also supported.
npm install openid-client --save
Quick start
Discover an Issuer configuration using its published .well-known endpoints
const { Issuer } = require('openid-client');
Issuer.discover('https://accounts.haravan.com/.well-known/openid-configuration') // => Promise
.then(function (haravanIssuer) {
console.log('Discovered issuer %s %O', haravanIssuer.issuer, haravanIssuer.metadata);
});
See the documentation for full API details.
Create a Client instance for that issuer's authorization server.
const client = new haravanIssuer.Client({
client_id: 'zELc...Oqas',
client_secret: 'TQV5...tny3',
redirect_uris: ['http://localhost:3000/cb'],
response_types: ['code id_token'],
// id_token_signed_response_alg (default "RS256")
}); // => Client
When you want to have your end-users authorize you need to send them to the issuer's authorization_endpoint
. Consult the web framework of your choice on how to redirect but here's how to get the authorization endpoint's URL with parameters already encoded in the query to redirect to.
const { generators } = require('openid-client');
const nonce = generators.nonce();
// store the nonce in your framework's session mechanism, if it is a cookie based solution
// it should be httpOnly (not readable by javascript) and encrypted.
let authUrl = client.authorizationUrl({
scope: 'openid profile email org userinfo offline_access',
response_mode: 'form_post',
nonce: nonce,
});
res.redirect(authUrl);
When end-users hit back your redirect_uri
with a POST (authorization request included form_post
response mode) your application consumes the callback and passes the nonce
in to include it in the ID Token verification steps.
const { custom } = require('openid-client');
const CLOCK_TOLERANCE = custom.clock_tolerance;
// assumes req.body is populated from your web framework's body parser
const params = client.callbackParams(req);
params.client_id = 'zELc...Oqas';
params.client_secret = 'TQV5...tny3';
params.grant_type = 'authorization_code';
client[CLOCK_TOLERANCE] = 10;
client.callback('http://localhost:3000/cb', params, { nonce }) // => Promise
.then(function (tokenSet) {
console.log('received and validated tokens %j', tokenSet);
console.log('validated ID Token claims %j', tokenSet.claims());
console.log('received Access token: %s', tokenSet.access_token);
});
//nonce,redirect_uri must have same value in authorizationUrl
You can then call the userinfo_endpoint
.
client.userinfo(tokenSet.access_token) // => Promise
.then(function (userinfo) {
console.log('userinfo %j', userinfo);
});
And later refresh the tokenSet if it had a refresh_token
.
client.refresh(tokenSet.refresh_token) // => Promise
.then(function (tokenSet) {
console.log('refreshed and validated tokens %j', tokenSet);
console.log('refreshed ID Token claims %j', tokenSet.claims());
});
See the documentation for more informations.