A generic OpenID Connect (OIDC) client library for JavaScript and TypeScript, designed to work with PingOne platforms.
The oidc module follows the OIDC specification and provides a simple and easy-to-use API to interact with the OIDC server. It allows you to authenticate, retrieve the access token, revoke the token, and sign out from the OIDC server.
pnpm add @forgerock/oidc-client
# or
npm install @forgerock/oidc-client
# or
yarn add @forgerock/oidc-client
import { oidc } from '@forgerock/oidc-client';
import { OidcConfig, OidcClient } from '@forgerock/oidc-client/types';
const config: OidcConfig = {
serverConfig: { wellknown: 'https://example.com/.well-known/openid-configuration' },
clientId: 'example-client-id',
redirectUri: 'https://example-app/redirect-uri',
scope: 'openid profile email',
};
const oidcClient: OidcClient = await oidc({ config });
The oidc() initialization function accepts the following configuration:
'openid profile email')See OidcConfig interface for the full list of options.
Here's a minimal example to get started:
import { oidc } from '@forgerock/oidc-client';
// Initialize the client
const oidcClient = await oidc({ config });
// Start authorization in the background
const authResponse = await oidcClient.authorize.background();
// Get tokens
const tokens = await oidcClient.token.exchange(authResponse.code, authResponse.state);
// Get user information
const user = await oidcClient.user.info();
// Clean up: logout and revoke tokens
await oidcClient.user.logout();
Methods for creating and handling authorization flows.
authorize.url(options?)Creates an authorization URL with the provided options or defaults from the configuration.
GetAuthorizationUrlOptions (optional)Promise<string | GenericError> - The authorization URL or an errorconst authUrl = await oidcClient.authorize.url();
authorize.background(options?)Initiates the authorization process in the background, returning the authorization code and state or an error. This method handles the authorization flow without requiring user interaction.
GetAuthorizationUrlOptions (optional)Promise<AuthorizationSuccess | AuthorizationError> - An object containing code and state on success, or error details on failureconst authResponse = await oidcClient.authorize.background();
Methods for managing OAuth tokens.
token.exchange(code, state, options?)Exchanges an authorization code for tokens using the token endpoint from the wellknown configuration. The tokens are automatically stored in the configured storage.
code (string) - The authorization code received from the authorization serverstate (string) - The state parameter from the authorization URL creationoptions (Partial<StorageConfig>, optional) - Storage configuration for persisting tokensPromise<OauthTokens | TokenExchangeErrorResponse | GenericError> - The new tokens or an errorconst tokens = await oidcClient.token.exchange(authCode, authState);
token.get(options?)Retrieves the current OAuth tokens from storage. Optionally auto-renews tokens if they are expired or if backgroundRenew is enabled.
GetTokensOptions (optional)
forceRenew - Force token renewal even if not expiredbackgroundRenew - Automatically renew expired tokensauthorizeOptions - Options for authorization during renewalstorageOptions - Storage configuration optionsPromise<OauthTokens | TokenExchangeErrorResponse | AuthorizationError | GenericError> - The tokens or an errorconst tokens = await oidcClient.token.get();
token.revoke()Revokes the access token using the revocation endpoint from the wellknown configuration. Requires an access token stored in the configured storage.
Promise<GenericError | RevokeSuccessResult | RevokeErrorResult> - Confirmation of revocation or an errorconst response = await oidcClient.token.revoke();
Methods for user information and session management.
user.info()Retrieves user information using the userinfo endpoint from the wellknown configuration. Requires an access token stored in the configured storage.
Promise<GenericError | UserInfoResponse> - User information object or an errorconst user = await oidcClient.user.info();
user.logout()Logs out the user by revoking tokens and clearing the storage. Uses the end session endpoint from the wellknown configuration.
Promise<GenericError | LogoutSuccessResult | LogoutErrorResult> - Confirmation of logout or an errorconst logoutResponse = await oidcClient.user.logout();
user.session(options?)Checks whether the user has an active session at the AS without prompting for login (prompt=none).
SessionCheckOptions (optional)Promise<SessionCheckSuccess | GenericError>const session = await oidcClient.user.session();
if ('error' in session) {
// No active session
}
responseType: 'none' (default)Use when you only need to know if a session is alive — no claims are returned.
Requires a stored id_token to send as id_token_hint. Fails immediately with no_id_token_hint if storage is empty.
How the check runs depends on redirectUri:
redirectUri (iframe): a hidden iframe loads the authorization URL. The AS redirects to redirectUri on success or appends error params on failure. The redirectUri must be same-origin — the browser blocks cross-origin iframe content access.redirectUri (fetch): a plain GET to the authorization endpoint — no iframe. The AS returns 204 on success or 400 on failure. Use this when no same-origin callback page is available.Returns { responseType: 'none' } on success.
responseType: 'id_token'Use when you need the user's claims back from the session. The AS issues a fresh id_token whose decoded claims are returned on success.
Always uses the iframe path — redirectUri is required. A stored id_token is sent as id_token_hint if available but is not required.
state and nonce are validated before claims are returned. If subject is provided, the sub claim must also match — otherwise any active session's claims are returned.
const session = await oidcClient.user.session({
responseType: 'id_token',
subject: knownUserId, // optional — omit to get claims for whoever is logged in
});
if (!('error' in session)) {
console.log(session.claims); // JWTPayload
}
Returns { responseType: 'id_token', claims: JWTPayload } on success.
PingOne SaaS does not support response_type=none. Its authorization server only accepts code, token, and id_token as response types — none is not listed as an option in the PingOne application configuration, the PingOne response types reference, the PingOne Platform API reference, or the PingOne support for response_mode options.
Use responseType: 'id_token' when connecting to PingOne.
authorize.url())Here's a practical example of implementing a redirect-based authentication flow. The user is redirected to the OIDC provider's login page:
import { oidc } from '@forgerock/oidc-client';
// 1. Initialize the client
const oidcClient = await oidc({ config });
// 2. Generate authorization URL and redirect user to OIDC provider
const authUrl = await oidcClient.authorize.url();
if (typeof authUrl !== 'string' && 'error' in authUrl) {
console.error('Failed to generate authorization URL:', authUrl.error);
} else {
// Redirect to OIDC provider's login page
window.location.assign(authUrl);
}
// After user logs in and is redirected back to your app with authorization code
// 3. Exchange authorization code for tokens
const urlParams = new URLSearchParams(window.location.search);
const code = urlParams.get('code');
const state = urlParams.get('state');
const tokens = await oidcClient.token.exchange(code, state);
if ('error' in tokens) {
console.error('Failed to exchange code for tokens:', tokens.error);
}
// 4. Retrieve user information
const userInfo = await oidcClient.user.info();
if ('error' in userInfo) {
console.error('Failed to fetch user info:', userInfo.error);
}
// 5. Later, when user wants to logout
const logoutResult = await oidcClient.user.logout();
if ('error' in logoutResult) {
console.error('Logout failed:', logoutResult.error);
}
authorize.background())Here's an example of initiating the authorization process in the background without user interaction. This method returns the authorization code and state directly:
import { oidc } from '@forgerock/oidc-client';
// 1. Initialize the client
const oidcClient = await oidc({ config });
// 2. Start authorization in the background
const authResponse = await oidcClient.authorize.background();
if ('error' in authResponse) {
console.error('Background authorization failed:', authResponse.error);
} else {
// 3. Exchange the authorization code for tokens
const tokens = await oidcClient.token.exchange(authResponse.code, authResponse.state);
if ('error' in tokens) {
console.error('Failed to exchange code for tokens:', tokens.error);
}
// 4. Retrieve user information
const userInfo = await oidcClient.user.info();
if ('error' in userInfo) {
console.error('Failed to fetch user info:', userInfo.error);
}
// 5. Later, when user wants to logout
const logoutResult = await oidcClient.user.logout();
if ('error' in logoutResult) {
console.error('Logout failed:', logoutResult.error);
}
}
Use automatic token renewal to keep the user's session valid. With the backgroundRenew option, this will either return valid tokens from storage if they exist or fetch new tokens if they are expired.
// Get tokens with automatic renewal if expired
const tokens = await oidcClient.token.get({
backgroundRenew: true,
});
if ('error' in tokens) {
console.error('Failed to retrieve tokens:', tokens.error);
} else {
console.log('Access token:', tokens.access_token);
}
The client supports Pushed Authorization Requests (PAR, RFC 9126), an OAuth 2.0 extension that hardens the authorization flow.
In a standard flow, all authorization parameters (redirect_uri, scope, PKCE code_challenge, state, and so on) are packed into the authorize URL and pass through the browser. With PAR, the client first POSTs those parameters directly to the server's PAR endpoint, receives a short-lived request_uri, and then sends the browser to the authorization endpoint with only client_id and request_uri in the URL:
Enabling PAR
PAR is enabled with the par configuration option:
const config = {
serverConfig: { wellknown: 'https://example.com/.well-known/openid-configuration' },
clientId: 'example-client-id',
redirectUri: 'https://example-app/redirect-uri',
scope: 'openid profile email',
par: true,
};
How it resolves:
par: true — always use PARpar: false — never use PAR; OIDC Client initialization fails if the server's well-known configuration advertises require_pushed_authorization_requests: truepar omitted — PAR is used automatically when the server requires it, otherwise the standard flow is usedBoth authorize.url() and authorize.background() respect this setting. The request_uri is single-use and short-lived (typically 30–90 seconds), so the authorize navigation must complete before it expires.
The client supports Rich Authorization Requests (RAR, RFC 9396), an OAuth 2.0 extension that lets the client request fine-grained, transaction-specific authorization instead of just scopes. Details are sent as the authorization_details request parameter — a JSON array describing what the client wants to do rather than a coarse scope string.
Each detail object has a type string defined by the authorization server, plus the standard fields locations, actions, datatypes, and privileges. Type-specific fields the SDK doesn't model go in additionalFields, whose entries are flattened onto the object before the request is sent.
Declaring authorization details
Details can be set at initialization, so they apply to every authorization request the client makes:
const config = {
serverConfig: { wellknown: 'https://example.com/.well-known/openid-configuration' },
clientId: 'example-client-id',
redirectUri: 'https://example-app/redirect-uri',
scope: 'openid profile email',
authorizationDetails: [
{
type: 'payment_initiation',
actions: ['initiate', 'status'],
locations: ['https://example.com/payments'],
instructedAmount: { currency: 'EUR', amount: '123.50' },
creditorName: 'Merchant A',
},
],
};
Type-specific fields like instructedAmount and creditorName can be passed through additionalFields:
const config = {
// ...other options
authorizationDetails: [
{
type: 'payment_initiation',
actions: ['initiate', 'status'],
locations: ['https://example.com/payments'],
additionalFields: {
instructedAmount: { currency: 'EUR', amount: '123.50' },
creditorName: 'Merchant A',
},
},
],
};
Authorization details can alternatively be supplied per call, which overrides the OIDC Client configuration for that request:
const authResponse = await oidcClient.authorize.background({
authorizationDetails: [
{
type: 'account_information',
actions: ['list', 'read'],
locations: ['https://example.com/accounts'],
datatypes: ['balances', 'transactions'],
},
],
});
Details are serialized to JSON for the authorization_details parameter and are included in both the standard flow and the pushed (PAR) request body. Server support for RAR is required; consult your authorization server's documentation for the detail type values and fields it accepts.
The library uses a consistent error handling pattern. All methods return either a success response or an error object. Check if the response contains an error property:
// Pattern for handling responses
const result = await oidcClient.user.info();
if ('error' in result) {
// Handle error case
console.error('Error:', result.error);
console.error('Error description:', result.error_description);
} else {
// Handle success case
console.log('User:', result);
}