What This Is
oidc-spa is an OpenID Connect client for browser-first web apps. It implements the Authorization Code Flow with PKCE and supports DPoP. It also ships token validation utilities for JavaScript backends.
It includes security defenses to reduce token exposure risks in the browser.
It’s one dependency free library for the full stack. It can replace frontend SDKs like keycloak-js, MSAL.js, or @auth0/auth0-spa-js. It can also replace backend token tooling like jsonwebtoken, jose, or express-jwt.
Why we built it
Most OIDC client libraries handle the basic sign-in flow well. But they leave you to implement:
Token renewal, and what happens on expiry.
Idle timeout UX. Auto-logout and re-auth prompts.
Login/logout sync across tabs.
Reliable session restore on reload, including third‑party cookie blocks.
Provider quirks. Keycloak, Entra ID, and Auth0 differ in practice.
We also wanted a TanStack-style developer experience:
Types flowing from config into the runtime API.
APIs that are hard to misuse.
Mockable OIDC for tests and “no-auth” / degraded environments.
So we built oidc-spa. It’s opinionated and high-level. It has few knobs by design.
It gives you enterprise-grade auth out of the box. So you can focus on your app.
Dive In
Ready to integrate? Start here.
Getting StartedPositioning
Here’s where oidc-spa sits compared to server-side OIDC:
Implementation
oidc-spa, keycloak-js, angular-oauth2-oidc, react-oidc-context, @auth0/auth0-spa-js, @azure/msal-browser, @axa-fr/oidc-client, oidc-client-ts (without client secret)
nuxt-oidc-auth, oidc-client-ts (with client secret), NextAuth/Auth.js/BetterAuth (often “roll your own auth” frameworks that can broker OIDC providers)
OIDC Model
The frontend is the OIDC client. Your backend API is an OAuth resource server. The frontend calls the API with an access token. The API can validate the token signature and resolve identity offline.
The backend is the OIDC client. User identity is tracked with session cookies. In this model, there is usually no OAuth resource server. Access tokens are mainly used for calling third-party APIs.
Infrastructure Requirement
None. The browser talks directly to the authorization server.
Requires a stateful backend and a shared session store (e.g. Redis).
Setup
Simple. Auth is decoupled from your app framework, router, and API.
Tightly coupled to a framework. You typically build login/logout routes and middleware.
Security
Historically weaker because tokens exist in the browser. With DPoP and modern defenses, the security gap can shrink significantly. See details.
Secure by design. Tokens are not exposed to frontend code.
Server-side rendering
Limited. The server renders without user context. Auth-aware UI renders on the client. See in practice.
Seamless. The server knows who the user is during render.
Is it a good fit for my stack?
It depends. oidc-spa is strong for client-side OIDC. But client-side OIDC isn’t the right model for every app.
When NOT to use oidc-spa
Avoid oidc-spa if you rely on SSR for auth-aware pages. This includes Next.js, Nuxt, SvelteKit, Remix/React Router Framework (non‑SPA mode), or Astro.
Those stacks push state and logic to the server. They also aim to ship minimal client JavaScript.
oidc-spa drives auth from the browser. That’s a mismatch for SSR-first architectures.
When you should use it
Use it for client-first apps. It works best when state and logic live in the browser.
Typically:
Vite + React (or another UI framework) - SPAs
TanStack Start (SSR works, but auth-aware UI renders client-side. See in practice)
Angular applications
Nuxt with
ssr: falseReact Router Framework with
ssr: false
If you’re choosing between this and a BFF for security, start with the security features. With those defenses enabled, the security profile can be comparable to server-side OIDC.
Last updated
Was this helpful?