OpenID Connect & OAuth2 Client (TypeScript)
oidc-client-ts is an actively maintained TypeScript library, currently at version 3.5.0, providing OpenID Connect (OIDC) and OAuth2 protocol support for client-side, browser-based JavaScript applications. It includes robust features for user session and access token management. This project is a direct fork of the unmaintained `IdentityModel/oidc-client-js`, ported to TypeScript with a largely similar API for its 2.0 release. Moving forward, it prioritizes OAuth 2.1 protocols and explicitly *does not* support the deprecated implicit grant. A key change since v3.x is the transition from `crypto-js` to the browser's native `crypto.subtle` module for cryptographic operations, which mandates the use of secure contexts (HTTPS). The library supports various flows, including Authorization Code Grant with PKCE, Authorization Code Grant, Resource Owner Password Credentials (ROPC) Grant, Refresh Token Grant, and Silent Refresh Token in iframe Flow.
Common errors
-
Oops... Crypto. Subtle is available only in secure contexts (HTTPS)
cause Attempting to run `oidc-client-ts` v3.x or later in an insecure HTTP context (non-localhost). The library relies on `window.crypto.subtle`, which browsers restrict to HTTPS or localhost.fixServe your application over HTTPS, even for development, or ensure you are developing on `localhost`. -
Error: No matching redirect URI found
cause The `redirect_uri` configured in `UserManagerSettings` does not exactly match a pre-registered redirect URI on your OIDC/OAuth2 Identity Provider, or the response from the IdP is redirecting to an unrecognized URI.fixVerify that the `redirect_uri` in your `UserManager` configuration is identical to one registered with your Identity Provider, including scheme (http/https), hostname, and path. Check your browser's network tab for the exact redirect URL used. -
PKCE code_verifier was not found
cause This typically occurs when the OIDC flow initiated with PKCE cannot complete because the `code_verifier` (a cryptographically random string generated at the start of the flow) is lost or unavailable during the callback. This can happen due to incorrect state store configuration, browser issues (e.g., third-party cookie blocking for iframes), or rapid page navigations.fixEnsure `WebStorageStateStore` is correctly configured and accessible (e.g., using `localStorage` instead of `sessionStorage` if session persistence across tabs/windows is needed). Check for browser settings that might block storage access in iframes for silent renews. Review application navigation logic to prevent premature state loss. -
Cannot read properties of undefined (reading 'access_token') after sign-in
cause This often indicates that the `User` object returned from the `signinRedirectCallback()` or `getUser()` method is `null` or `undefined`, meaning the authentication flow did not successfully complete or no user session was found. This could be due to an error during the OIDC response processing, an invalid token, or the user being unauthenticated.fixImplement robust error handling around `signinRedirectCallback()` and `getUser()`. Inspect the browser's console and network requests for errors during the OIDC flow. Ensure all required scopes are requested and granted, and that the IdP is correctly issuing tokens. Check `Log.setLevel(Log.DEBUG)` for more verbose output from `oidc-client-ts`.
Warnings
- breaking Since v3.0.0, `oidc-client-ts` no longer uses `crypto-js` and instead relies on the browser's native `crypto.subtle` module. This change mandates that your application must run in a 'secure context' (HTTPS). Using HTTP, even for local development (unless `localhost`), will result in errors.
- breaking `oidc-client-ts` is a TypeScript port and successor to `IdentityModel/oidc-client-js`, which halted development in June 2021. While the API is largely similar to `oidc-client-js` v1.x, `oidc-client-ts` v2.0.0 removed support for the deprecated implicit grant flow, focusing exclusively on OAuth 2.1 compliant flows like the Authorization Code Grant with PKCE.
- gotcha The `UserManagerSettings` object has several properties that were renamed or had their default values changed between `oidc-client-js` v1.x and `oidc-client-ts` v2.0.0, and again from v2.x to v3.x. For example, `clockSkew` became `clockSkewInSeconds`, `loadUserInfo` default changed from `true` to `false`, and `revokeAccessTokenOnSignout` became `revokeTokensOnSignout`.
- gotcha Proper configuration of `authority`, `client_id`, and `redirect_uri` is crucial. The `redirect_uri` must be precisely registered with your OIDC/OAuth2 provider and match the one used in the client application, including scheme (HTTP/HTTPS), hostname, and path.
Install
-
npm install oidc-client-ts -
yarn add oidc-client-ts -
pnpm add oidc-client-ts
Imports
- UserManager
const UserManager = require('oidc-client-ts').UserManager;import { UserManager } from 'oidc-client-ts'; - WebStorageStateStore
import { WebStorageStateStore } from 'oidc-client-ts/src/WebStorageStateStore';import { WebStorageStateStore } from 'oidc-client-ts'; - Log
const Log = require('oidc-client-ts').Log;import { Log } from 'oidc-client-ts';
Quickstart
import { UserManager, WebStorageStateStore, Log } from 'oidc-client-ts';
// Configure logging for better debugging
Log.setLevel(Log.INFO);
Log.setLogger(console);
const userManagerSettings = {
authority: 'https://demo.identityserver.io',
client_id: 'interactive.public.js',
redirect_uri: 'http://localhost:5003/signin-callback.html',
post_logout_redirect_uri: 'http://localhost:5003/signout-callback.html',
response_type: 'code',
scope: 'openid profile api offline_access',
// Use PKCE for enhanced security with authorization code flow
client_secret: process.env.OIDC_CLIENT_SECRET ?? '', // Only required for confidential clients
userStore: new WebStorageStateStore({ store: window.localStorage }),
automaticSilentRenew: true,
// Ensure this matches the origin of your app, especially for silent renew iframes
monitorSession: true,
checkSessionIntervalInSeconds: 3,
};
const userManager = new UserManager(userManagerSettings);
// Handle the signin redirect callback in your callback page
async function handleSigninCallback() {
try {
const user = await userManager.signinRedirectCallback();
console.log('Signed in:', user);
// Redirect to your application's main page or show user info
} catch (error) {
console.error('Error handling signin callback:', error);
}
}
// Trigger signin (e.g., from a login button)
async function signIn() {
try {
await userManager.signinRedirect();
} catch (error) {
console.error('Error initiating signin:', error);
}
}
// Example usage (assuming a simple HTML page):
// On your main page:
// document.getElementById('loginButton').addEventListener('click', signIn);
// On your signin-callback.html page:
// window.onload = handleSigninCallback;
// To simulate usage for demonstration:
if (window.location.pathname === '/signin-callback.html') {
handleSigninCallback();
} else {
console.log('User Manager initialized. Call signIn() to start login flow.');
// For demonstration, directly call signIn if not on callback page
// In a real app, this would be triggered by user action
// signIn();
}
export { userManager, signIn, handleSigninCallback };