What is openid-client?
The openid-client package is a server-side library that allows Node.js applications to act as a Relying Party (RP) for any OpenID Connect (OIDC) compliant Identity Provider (IP). It provides functionality to discover OIDC providers, authenticate users, validate ID tokens, and securely perform token operations.
What are openid-client's main functionalities?
Discovery of OpenID Provider configuration
This feature allows the client to automatically discover the OpenID Provider's configuration using the issuer's URL. It simplifies the process of setting up the client by fetching the necessary endpoints and public keys.
const { Issuer } = require('openid-client');
(async () => {
const googleIssuer = await Issuer.discover('https://accounts.google.com');
console.log('Discovered issuer %s', googleIssuer.issuer);
})();
Client authentication with an OpenID Provider
This code sample demonstrates how to authenticate with an OpenID Provider by creating a client instance with the necessary credentials and generating an authorization URL for user redirection.
const { Issuer } = require('openid-client');
(async () => {
const issuer = await Issuer.discover('https://example.com');
const client = new issuer.Client({
client_id: 'your-client-id',
client_secret: 'your-client-secret',
redirect_uris: ['https://your-callback-url/callback'],
response_types: ['code']
});
const authorizationUrl = client.authorizationUrl({
scope: 'openid email profile',
});
console.log('Authorization URL:', authorizationUrl);
})();
Handling authentication responses
This feature is used to handle the callback from the OpenID Provider after user authentication. It involves parsing the callback parameters, exchanging the authorization code for tokens, and validating the ID token.
const { Issuer, generators } = require('openid-client');
(async () => {
const issuer = await Issuer.discover('https://example.com');
const client = new issuer.Client({
client_id: 'your-client-id',
client_secret: 'your-client-secret',
redirect_uris: ['https://your-callback-url/callback'],
response_types: ['code']
});
const code_verifier = generators.codeVerifier();
const code_challenge = generators.codeChallenge(code_verifier);
const params = client.callbackParams('https://your-callback-url/callback?code=AUTH_CODE&state=STATE');
const tokenSet = await client.callback('https://your-callback-url/callback', params, { code_verifier });
console.log('Received and validated tokens %j', tokenSet);
console.log('ID Token claims %j', tokenSet.claims());
})();
Token management
This code sample shows how to manage tokens, including how to exchange an authorization code for tokens and how to refresh tokens using a refresh token.
const { Issuer } = require('openid-client');
(async () => {
const issuer = await Issuer.discover('https://example.com');
const client = new issuer.Client({
client_id: 'your-client-id',
client_secret: 'your-client-secret'
});
const tokenSet = await client.grant({
grant_type: 'authorization_code',
code: 'AUTH_CODE',
redirect_uri: 'https://your-callback-url/callback'
});
const refreshedTokenSet = await client.refresh(tokenSet.refresh_token);
console.log('Refreshed tokens %j', refreshedTokenSet);
})();
Other packages similar to openid-client
passport
Passport is a widely used authentication middleware for Node.js. It supports a wide range of strategies, including OpenID Connect. Compared to openid-client, Passport provides a more general authentication solution that can be extended with various strategies for different authentication mechanisms.
oidc-provider
oidc-provider is a Node.js library that allows developers to implement their own OpenID Connect Provider. It is different from openid-client, which is designed to be used as a client for existing providers. oidc-provider is more suitable for those who want to create an identity provider rather than connect to one.
openid-client
openid-client is a server side OpenID Relying Party (RP, Client) implementation for
Node.js
Table of Contents
Implemented specs & features
The following client/RP features from OpenID Connect/OAuth2.0 specifications are implemented by
openid-client.
Example
Head over to the example folder to see the library in use. This example is deployed and configured
to use an example OpenID Connect Provider here. The provider is using
oidc-provider library.
Get started
On the off-chance you want to manage multiple clients for multiple issuers you need to first get
an Issuer instance.
via Discovery (recommended)
const Issuer = require('openid-client').Issuer;
Issuer.discover('https://accounts.google.com')
.then(function (googleIssuer) {
console.log('Discovered issuer %s', googleIssuer);
});
manually
const Issuer = require('openid-client').Issuer;
const googleIssuer = new Issuer({
issuer: 'https://accounts.google.com',
authorization_endpoint: 'https://accounts.google.com/o/oauth2/v2/auth',
token_endpoint: 'https://www.googleapis.com/oauth2/v4/token',
userinfo_endpoint: 'https://www.googleapis.com/oauth2/v3/userinfo',
jwks_uri: 'https://www.googleapis.com/oauth2/v3/certs',
});
console.log('Set up issuer %s', googleIssuer);
Now you can create your Client.
manually (recommended)
You should provide the following metadata; client_id, client_secret
. You can also provide
id_token_signed_response_alg
(defaults to RS256
) and token_endpoint_auth_method
(defaults to
client_secret_basic
);
const client = new googleIssuer.Client({
client_id: 'zELcpfANLqY7Oqas',
client_secret: 'TQV5U29k1gHibH5bx1layBo0OSAvAbRT3UYW3EWrSYBB5swxjVfWUa1BS8lqzxG/0v9wruMcrGadany3'
});
via registration client uri
Should your oidc provider have provided you with a registration client uri and registration access
token you can also have the Client discovered.
new googleIssuer.Client.fromUri(registration_client_uri, registration_access_token)
.then(function (client) {
console.log('Discovered client %s', client);
});
Usage
Getting authorization url
client.authorizationUrl({
redirect_uri: 'https://client.example.com/callback',
scope: 'openid email',
});
Getting authorization url
client.authorizationUrl({
redirect_uri: 'https://client.example.com/callback',
scope: 'openid email',
});
Processing callback
client.authorizationCallback('https://client.example.com/callback', request.query)
.then(function (tokens) {
console.log('received tokens %j', tokens);
});
Processing callback with state or nonce check
const state = session.state;
const nonce = session.nonce;
client.authorizationCallback('https://client.example.com/callback', request.query, { state, nonce })
.then(function (tokens) {
console.log('received tokens %j', tokens);
});
Refreshing a token
client.refresh(refreshToken)
.then(function (tokens) {
console.log('refreshed tokens %j', tokens);
});
Revoke a token
client.revoke(token)
.then(function () {
console.log('revoked token %s', token);
});
Introspect a token
client.introspect(token)
.then(function (details) {
console.log('token details %j', details);
});
Fetching userinfo
client.userinfo(accessToken)
.then(function (userinfo) {
console.log('userinfo %j', userinfo);
});
via POST
client.userinfo(accessToken, { verb: 'post' });
auth via query
client.userinfo(accessToken, { via: 'query' });
auth via body
client.userinfo(accessToken, { verb: 'post', via: 'body' });
userinfo also handles (as long as you have the proper metadata configured) responses that are:
- signed
- signed and encrypted (nested JWT)
- just encrypted
Custom token endpoint grants
Use when the token endpoint also supports client_credentials or password grants;
client.grant({
grant_type: 'client_credentials'
});
client.grant({
grant_type: 'password',
username: 'johndoe',
password: 'A3ddj3w',
});
Registering new client (via Dynamic Registration)
issuer.Client.register(metadata, [keystore])
.then(function (client) {
console.log('Registered client %s, %j', client, client.metadata);
});
Configuration
Changing HTTP request defaults
Setting defaultHttpOptions
on Issuer
always merges your passed options with the default. openid-client uses got for http requests with the following default request options
const DEFAULT_HTTP_OPTIONS = {
followRedirect: false,
headers: { 'User-Agent': `${pkg.name}/${pkg.version} (${pkg.homepage})` },
retries: 0,
timeout: 1500,
};
You can add your own headers, change the user-agent used or change the timeout setting
Issuer.defaultHttpOptions = { timeout: 2500, headers: { 'X-Your-Header': '<whatever>' } };
Confirm your httpOptions by
console.log('httpOptions %j', Issuer.defaultHttpOptions);