openid-client is a server side OpenID Relying Party (RP, Client) implementation for Node.js
Table of Contents
The following client/RP features from OpenID Connect/OAuth2.0 specifications are implemented by openid-client.
- OpenID Connect Core 1.0 incorporating errata set 1
- Authorization Callback
- Authorization Code Flow
- Implicit Flow
- Hybrid Flow
- UserInfo Request
- Fetching Distributed Claims
- Unpacking Aggregated Claims
- Offline Access / Refresh Token Grant
- Client Credentials Grant
- Password Grant
- Client Authentication
- client_secret_basic
- client_secret_post
- client_secret_jwt
- private_key_jwt
- Authorization Callback
- OpenID Connect Discovery 1.0 incorporating errata set 1
- Discovery of OpenID Provider (Issuer) Metadata
- OpenID Connect Dynamic Client Registration 1.0 incorporating errata set 1
- Dynamic Client Registration request
- Client initialization via registration client uri
- RFC7009 - OAuth 2.0 Token revocation
- Client Authenticated request to token revocation
- RFC7662 - OAuth 2.0 Token introspection
- Client Authenticated request to token introspection
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.
On the off-chance you want to manage multiple clients for multiple issuers you need to first get an Issuer instance.
const Issuer = require('openid-client').Issuer;
Issuer.discover('https://accounts.google.com') // => Promise
.then(function (googleIssuer) {
console.log('Discovered issuer %s', googleIssuer);
});
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',
}); // => Issuer
console.log('Set up issuer %s', googleIssuer);
Now you can create your Client.
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'
}); // => Client
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) // => Promise
.then(function (client) {
console.log('Discovered client %s', client);
});
client.authorizationUrl({
redirect_uri: 'https://client.example.com/callback',
scope: 'openid email',
}); // => String
client.authorizationCallback('https://client.example.com/callback', request.query) // => Promise
.then(function (tokenSet) {
console.log('received tokens %j', tokenSet);
});
const state = session.state;
const nonce = session.nonce;
client.authorizationCallback('https://client.example.com/callback', request.query, { state, nonce }) // => Promise
.then(function (tokenSet) {
console.log('received tokens %j', tokenSet);
});
client.refresh(refreshToken) // => Promise
.then(function (tokenSet) {
console.log('refreshed tokens %j', tokenSet);
});
Tip: accepts TokenSet as well as direct refresh token values;
client.revoke(token, [tokenTypeHint]) // => Promise
.then(function (response) {
console.log('revoked token %s', token, response);
});
client.introspect(token, [tokenTypeHint]) // => Promise
.then(function (response) {
console.log('token details %j', response);
});
client.userinfo(accessToken) // => Promise
.then(function (userinfo) {
console.log('userinfo %j', userinfo);
});
Tip: accepts TokenSet as well as direct access token values;
via POST
client.userinfo(accessToken, { verb: 'post' }); // => Promise
auth via query
client.userinfo(accessToken, { via: 'query' }); // => Promise
auth via body
client.userinfo(accessToken, { verb: 'post', via: 'body' }); // => Promise
userinfo also handles (as long as you have the proper metadata configured) responses that are:
- signed
- signed and encrypted (nested JWT)
- just encrypted
let claims = {
sub: 'userID',
_claim_names: {
credit_history: 'src1',
email: 'src2',
},
_claim_sources: {
src1: { endpoint: 'https://src1.example.com/claims', access_token: 'foobar' },
src2: { endpoint: 'https://src2.example.com/claims' },
},
};
client.fetchDistributedClaims(claims, { src2: 'bearer.for.src2' }) // => Promise
.then(function (output) {
console.log('claims %j', claims); // ! also modifies original input, does not create a copy
console.log('output %j', output);
// removes fetched names and sources and removes _claim_names and _claim_sources members if they
// are empty
});
// when rejected the error will have a property 'src' with the source name it relates to
let claims = {
sub: 'userID',
_claim_names: {
credit_history: 'src1',
email: 'src2',
},
_claim_sources: {
src1: { JWT: 'probably.a.jwt' },
src2: { JWT: 'probably.another.jwt' },
},
};
client.unpackAggregatedClaims(claims) // => Promise, autodiscovers JWT issuers, verifies signatures
.then(function (output) {
console.log('claims %j', claims); // ! also modifies original input, does not create a copy
console.log('output %j', output);
// removes fetched names and sources and removes _claim_names and _claim_sources members if they
// are empty
});
// when rejected the error will have a property 'src' with the source name it relates to
Use when the token endpoint also supports client_credentials or password grants;
client.grant({
grant_type: 'client_credentials'
}); // => Promise
client.grant({
grant_type: 'password',
username: 'johndoe',
password: 'A3ddj3w',
}); // => Promise
issuer.Client.register(metadata, [keystore]) // => Promise
.then(function (client) {
console.log('Registered client %s, %j', client, client.metadata);
});
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);