Security News
Research
Data Theft Repackaged: A Case Study in Malicious Wrapper Packages on npm
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
openid-client
Advanced tools
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.
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);
})();
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 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.
OAuth 2 / OpenID Connect Client API for JavaScript Runtimes
openid-client simplifies integration with authorization servers by providing easy-to-use APIs for the most common authentication and authorization flows, including OAuth 2 and OpenID Connect. It is designed for JavaScript runtimes like Node.js, Browsers, Deno, Cloudflare Workers, and more.
The following features are currently in scope and implemented in this software:
If you want to quickly add authentication to JavaScript apps, feel free to check out Auth0's JavaScript SDK and free plan. Create an Auth0 account; it's free!
Filip Skokan has certified that this software conforms to the Basic, FAPI 1.0, and FAPI 2.0 Relying Party Conformance Profiles of the OpenID Connect™ protocol.
Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by becoming a sponsor.
openid-client
is distributed via npmjs.com and github.com.
example
ESM import
import * as client from 'openid-client'
let server!: URL // Authorization Server's Issuer Identifier
let clientId!: string // Client identifier at the Authorization Server
let clientSecret!: string // Client Secret
let config: client.Configuration = await client.discovery(
server,
clientId,
clientSecret,
)
Authorization Code flow is for obtaining Access Tokens (and optionally Refresh Tokens) to use with third party APIs.
When you want to have your end-users authorize or authenticate you need to send them to the authorization server'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.
/**
* Value used in the authorization request as the redirect_uri parameter, this
* is typically pre-registered at the Authorization Server.
*/
let redirect_uri!: string
let scope!: string // Scope of the access request
/**
* PKCE: The following MUST be generated for every redirect to the
* authorization_endpoint. You must store the code_verifier and state in the
* end-user session such that it can be recovered as the user gets redirected
* from the authorization server back to your application.
*/
let code_verifier: string = client.randomPKCECodeVerifier()
let code_challenge: string =
await client.calculatePKCECodeChallenge(code_verifier)
let state!: string
let parameters: Record<string, string> = {
redirect_uri,
scope,
code_challenge,
code_challenge_method: 'S256',
}
if (!config.serverMetadata().supportsPKCE()) {
/**
* We cannot be sure the server supports PKCE so we're going to use state too.
* Use of PKCE is backwards compatible even if the AS doesn't support it which
* is why we're using it regardless. Like PKCE, random state must be generated
* for every redirect to the authorization_endpoint.
*/
state = client.randomState()
parameters.state = state
}
let redirectTo: URL = client.buildAuthorizationUrl(config, parameters)
// now redirect the user to redirectTo.href
console.log('redirecting to', redirectTo.href)
When end-users are redirected back to the redirect_uri
your application consumes the callback and
passes in PKCE code_verifier
to include it in the authorization code grant token exchange.
let getCurrentUrl!: (...args: any) => URL
let tokens: client.TokenEndpointResponse = await client.authorizationCodeGrant(
config,
getCurrentUrl(),
{
pkceCodeVerifier: code_verifier,
expectedState: state,
},
)
console.log('Token Endpoint Response', tokens)
You can then fetch a protected resource response
let protectedResourceResponse: Response = await client.fetchProtectedResource(
config,
tokens.access_token,
new URL('https://rs.example.com/api'),
'GET',
)
console.log(
'Protected Resource Response',
await protectedResourceResponse.json(),
)
let scope!: string // Scope of the access request
let response = await client.initiateDeviceAuthorization(config, { scope })
console.log('User Code:', response.user_code)
console.log('Verification URI:', response.verification_uri)
console.log('Verification URI (complete):', response.verification_uri_complete)
You will display the instructions to the end-user and have them directed at verification_uri
or
verification_uri_complete
, afterwards you can start polling for the Device Access Token Response.
let tokens: client.TokenEndpointResponse =
await client.pollDeviceAuthorizationGrant(config, response)
console.log('Token Endpoint Response', tokens)
This will poll in a regular interval and only resolve with tokens once the end-user authenticates.
Client Credentials flow is for obtaining Access Tokens to use with third party APIs on behalf of your application, rather than an end-user which was the case in previous examples.
let scope!: string // Scope of the access request
let resource!: string // Resource Indicator of the Resource Server the access token is for
let tokens: client.TokenEndpointResponse = await lib.clientCredentialsGrant(
config,
{ scope, resource },
)
console.log('Token Endpoint Response', tokens)
The supported JavaScript runtimes include those that support the utilized Web API globals and standard built-in objects. These are (but are not limited to):
Version | Security Fixes 🔑 | Other Bug Fixes 🐞 | New Features ⭐ | Runtime and Module type |
---|---|---|---|---|
v6.x | ✅ | ✅ | ✅ | Universal2 ESM3 |
v5.x | ✅ | ❌ | ❌ | Node.js CJS + ESM |
Node.js v20.x as baseline is required ↩
Assumes runtime support of WebCryptoAPI and Fetch API ↩
CJS style require('openid-client')
is possible in Node.js versions where process.features.require_module
is true
or with the --experimental-require-module
Node.js CLI flag. ↩
FAQs
OAuth 2 / OpenID Connect Client API for JavaScript Runtimes
We found that openid-client demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Research
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Research
Security News
Attackers used a malicious npm package typosquatting a popular ESLint plugin to steal sensitive data, execute commands, and exploit developer systems.
Security News
The Ultralytics' PyPI Package was compromised four times in one weekend through GitHub Actions cache poisoning and failure to rotate previously compromised API tokens.