Skip to main content

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.