| icon | layout | metaLinks | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
sign-posts-wrench |
|
{% hint style="info" %} If you're having issues, don't hesitate to reach out on Discord. We're here to help! {% endhint %}
oidc-spa is an OpenID Connect client for browser-centric web apps. It implements the Authorization Code Flow with PKCE+DPoP and also provides token validation utilities for JavaScript backends.
It features a set of security defences that make it stand out compared to other Client-Side OIDC implementations.
It’s a single library that can replace platform-specific SDKs like keycloak-js, MSAL.js, @auth0/auth0-spa-js, etc. on the frontend, and jsonwebtoken, jose or express-jwt on your JS backend.
Convinced this is what you're looking for already? Let's get your app authenticated.
{% content-ref url="integration-guides/example-setups.md" %} example-setups.md {% endcontent-ref %}
Here is a comparison to help you understand better where oidc-spa sits:
| Client-Side OIDC | 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 (Ish they are "roll your own auth" solutions that can funnel OIDC providers) |
| OIDC Model | The frontend code is the OIDC client. Your backend API is an OAuth resource server. The frontend makes requests with the access token in the header to the API. The API can resolve the identity offline by validating the signature of the token. | The backend is the OIDC client. The user identity of the user is tracked across requests with session cookies. In this model there is usually no notion of an OAuth resource server. The access token is not used unless you call third-party services. |
| Infra requirement | None. The browser talks directly to the Auth Server. | Requires a stateful backend and a store (eg redis) to share session across server replicas. |
| Set up simplicity | Very easy. Auth concerns are decoupled from your app framework, routing library and backend API. | Strongly coupled with a specific framework, need to create login/logout routes and set up middlewares. |
| Security | Historically much weaker, tokens are exposed to the frontend code. Today, with DPoP and other modern defences, there is a case to be made that both security profiles are equivalent. Discussed here. | Secure by design. The tokens are never exposed to the frontend. |
| Server Side Rendering | Limited. The server does not know who the user is when rendering the pages. Only public pages and global layout can be SSR'd. Auth aware component rendering must be delegated to the client. | Seamless. The server knows who the user is. |
It depends, while oidc-spa clearly stands out compared to other solutions in the Client-Side OIDC category, client-side OIDC is not the right model for all applications.
If you use a full stack JS framework with SSR enabled: Next.js, Nuxt, SvelteKit, Remix/React Router Framework (not in SPA mode) or Astro. oidc-spa is probably not a good choice.
Those frameworks' end goal is to move as much of the states and logic to the backend and send as little JavaScript to the client as possible.
In oidc-spa the auth is driven by the frontend. There is a philosophy mismatch here.
Basically any client-first web application. Highly interactive applications where states and logic primarily live in the frontend.
Typically:
- Vite + React (or another UI framework) - Single Page Applications (SPAs)
- TanStack Start - SSR support but rendering of auth-aware components happens client-side.
- Angular applications
- Nuxt with SSR: false
- React Router Framework with SSR false.
If you're hesitating between this and implementing a BFF pattern for security considerations you can check out the security features of oidc-spa. With those defences enabled, the security profile of applications matches those implementing server-side OIDC.
Onboard? Let's get your app authenticated!
{% content-ref url="integration-guides/example-setups.md" %} example-setups.md {% endcontent-ref %}