Safeguard
Security

oidc-client-ts: A Security Guide for Browser OIDC

oidc-client-ts is the maintained TypeScript library for adding OpenID Connect and OAuth2 to browser apps. Here is how to use it, and how to avoid the token-handling mistakes that undo its security.

Yukti Singhal
Platform Engineer
6 min read

oidc-client-ts is the actively maintained TypeScript library for implementing OpenID Connect (OIDC) and OAuth2 in browser-based JavaScript applications, and it defaults to the Authorization Code flow with PKCE — the flow you should be using. If you are working from an older tutorial that mentions oidc-client (no -ts), you are looking at the predecessor; that project halted development in 2021, and oidc-client-ts is its TypeScript successor with a similar API.

The library handles the fiddly parts of the OIDC dance: redirects, PKCE code verifiers, token exchange, session monitoring, and silent renewal. It does that job well. The security outcomes still depend heavily on how you configure it and where you decide to keep tokens.

What the library does

At its core, oidc-client-ts gives you a UserManager that drives the login flow and holds the resulting user session. A minimal browser setup:

import { UserManager, WebStorageStateStore } from 'oidc-client-ts';

const userManager = new UserManager({
  authority: 'https://idp.example.com',
  client_id: 'my-spa',
  redirect_uri: 'https://app.example.com/callback',
  response_type: 'code',            // Authorization Code flow
  scope: 'openid profile email',
  userStore: new WebStorageStateStore({ store: window.sessionStorage }),
});

// kick off login
await userManager.signinRedirect();

On the callback page you complete the exchange:

const user = await userManager.signinRedirectCallback();
// user.access_token, user.id_token, user.profile

Because response_type is code, the library automatically uses PKCE (Proof Key for Code Exchange). PKCE binds the authorization request to the token exchange with a one-time secret, which is what makes the Authorization Code flow safe for a public client that cannot keep a client secret. This is the current recommended flow for single-page apps; the older Implicit flow, which returned tokens directly in the URL fragment, is discouraged and you should not enable it.

The real security decision: where do tokens live

This is where most of the risk sits, and the library cannot make the choice for you. A browser SPA has to keep the access and ID tokens somewhere, and every option has a tradeoff against cross-site scripting (XSS).

  • localStorage persists across tabs and restarts, which is convenient, but it is readable by any JavaScript running on the page. If an attacker lands XSS, they read the tokens directly.
  • sessionStorage is scoped to the tab and cleared when it closes, slightly reducing exposure, but it is still JavaScript-readable and still falls to XSS.
  • In-memory storage (a plain variable) is not readable from storage APIs and disappears on refresh, which is the most defensive but means re-authenticating (often via a silent renew) after a reload.

There is no configuration that makes browser-stored tokens immune to XSS, because any script on your origin can reach anything the app can reach. The honest conclusion the OAuth community has reached is that the strongest pattern is the Backend-for-Frontend (BFF): tokens are held server-side and the browser holds only a secure, HttpOnly session cookie. oidc-client-ts is a pure-browser library, so if you use it directly, you have accepted browser token storage and your primary defense becomes preventing XSS in the first place.

Given that, the ranking is: prefer in-memory or sessionStorage over localStorage, and put serious effort into a content security policy and output encoding so XSS never lands.

Validation you should not skip

The library validates tokens against the identity provider's discovery metadata and JWKS, which is exactly what you want — do not disable it. A few configuration points matter:

  • Keep authority pointed at an HTTPS issuer so discovery and key retrieval cannot be tampered with.
  • Do not turn off issuer or audience checks to "make it work." If validation fails, the token is genuinely wrong for your client.
  • Register your redirect_uri exactly at the provider and keep the list tight. Loose redirect URI matching is a well-known way to steal an authorization code.

Silent renewal and session monitoring

oidc-client-ts can renew tokens quietly using a hidden iframe or refresh tokens so the user is not bounced to the login page constantly. This is convenient but has moving parts. Refresh tokens in the browser are themselves sensitive; if you use them, prefer refresh token rotation at the provider so a leaked token is single-use. The iframe-based silent renew depends on third-party cookie behavior, which browsers have been steadily restricting, so test it against the browsers your users actually run rather than assuming it works.

Keeping the dependency itself honest

The library is one package, but it sits at your authentication boundary, which makes its supply chain worth watching. Pin the version, review updates rather than auto-bumping blindly, and keep an eye on its advisory feed. A software composition analysis scan will tell you if a version you depend on picks up a known CVE, and an SCA tool such as Safeguard can flag that transitively if the issue lives in one of its dependencies rather than the top-level package. Given how mature the library is, the more likely risk is your own configuration than a flaw in the code — but a security-critical dependency deserves the monitoring regardless.

A defensible baseline

If you are shipping oidc-client-ts to production:

  1. Use the Authorization Code flow with PKCE (the default). Never enable Implicit.
  2. Store tokens in memory or sessionStorage, not localStorage, and know that none of these survive XSS.
  3. Invest in a strong content security policy so XSS does not land in the first place.
  4. Keep token validation on, use HTTPS issuers, and register exact redirect URIs.
  5. If your threat model warrants it, move to a Backend-for-Frontend so tokens never touch the browser.

The library gets the protocol right. Your job is to not undo that with where you put the tokens.

FAQ

What is oidc-client-ts used for?

It is a TypeScript library for adding OpenID Connect and OAuth2 authentication to browser-based JavaScript applications. It handles login redirects, PKCE, token exchange, silent renewal, and session management.

Is oidc-client-ts the same as oidc-client?

No. oidc-client is the older predecessor that stopped active development in 2021. oidc-client-ts is the maintained TypeScript successor with a similar API and should be used for new projects.

Where should oidc-client-ts store tokens?

Prefer in-memory storage or sessionStorage over localStorage, since all browser storage is readable by JavaScript and vulnerable to XSS. For the strongest posture, use a Backend-for-Frontend so tokens stay server-side and the browser holds only an HttpOnly cookie.

Does oidc-client-ts use PKCE?

Yes. When configured for the Authorization Code flow (response_type: 'code'), the library uses PKCE automatically. This is the recommended flow for single-page applications; the older Implicit flow should not be used.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.