Skip to content

keycloakify/oidc-spa

Repository files navigation

oidc-spa


We're here to help!

Home - Documentation

oidc-spa is an OpenID Connect client built for browser-first apps.
It wraps the full Authorization Code + PKCE flow in a high-level API so you can ship secure app auth without stitching together multiple SDKs and ad-hoc glue.

  • 🔒 Security-first defaults: in-memory tokens, strict redirect handling, and opt-in defenses like DPoP and token substitution to reduce token exposure risk.
  • 🧭 Battle-tested auth UX: token renewal, idle timeout, auto login/logout, multi-tab session sync, and reliable session restore on reload.
  • 🧩 Full-stack ready: backend token validation utilities and first-class TanStack Start integration in the same library.
  • 🧰 Provider-aware: handles real-world quirks across Keycloak, Entra ID, Auth0, Google, and more.
  • ✨ Developer experience: types flow from config into the API, minimal knobs, and easy-to-mock auth for tests.

Get Started

At a glance

The Framework-Agnostic Adapter:

import { createOidc, oidcEarlyInit } from "oidc-spa/core"; // ~33 KB min+gzip (See: https://docs.oidc-spa.dev/resources/bundle-size)
import { z } from "zod"; // 59 KB min+gzip, but it's optional.

// Call this only if you don't use oidc-spa's Vite plugin.
oidcEarlyInit({ BASE_URL: "/" });

const oidc = await createOidc({
    issuerUri: "https://auth.my-domain.net/realms/myrealm",
    //issuerUri: "https://login.microsoftonline.com/...",
    //issuerUri: "https://xxx.us.auth0.com/..."
    //issuerUri: "https://accounts.google.com/o/oauth2/v2/auth"
    clientId: "myclient",
    // Optional; you can write a validator by hand, or give up some type-safety, your call.
    decodedIdTokenSchema: z.object({
        name: z.string(),
        picture: z.string().optional(),
        email: z.string(),
        realm_access: z.object({ roles: z.array(z.string()) })
    })
    // Yes, really, it's that simple; there are no other parameters to provide.
    // The Redirect URI (callback URL) is the root URL of your app (no public/callback.html involved).
});

// In oidc-spa the user is either logged in or they aren't.
// The state will never mutate without a full app reload.
// This makes reasoning about auth much, much easier.
if (!oidc.isUserLoggedIn) {
    await oidc.login();
    // Never here
    return;
}

const { name, realm_access } = oidc.getDecodedIdToken();

console.log(`Hello ${name}`);

const { accessToken } = await oidc.getTokens();

await fetch("https://my-domain.net/api/todos", {
    headers: {
        Authorization: `Bearer ${accessToken}`
    }
});

if (realm_access.roles.includes("realm-admin")) {
    // User is an admin
}

Higher-level adapters, example with React but we also feature a similar Angular adapter:

Image

Full-stack auth solution with TanStack Start:

import { createServerFn } from "@tanstack/react-start";
import { enforceLogin, oidcFnMiddleware } from "@/oidc";
import fs from "node:fs/promises";

const getTodos = createServerFn({ method: "GET" })
    .middleware([oidcFnMiddleware({ assert: "user logged in" })])
    .handler(async ({ context: { oidc } }) => {
        const userId = oidc.accessTokenClaims.sub;

        const json = await fs.readFile(`todos_${userId}.json`, "utf8");

        return JSON.parse(json);
    });

export const Route = createFileRoute("/todos")({
    beforeLoad: enforceLogin,
    loader: () => getTodos(),
    component: RouteComponent
});

function RouteComponent() {
    const todos = Route.useLoaderData();

    return (
        <ul>
            {todos.map(todo => (
                <li key={todo.id}>
                    {todo.isDone && "✅"} {todo.text}
                </li>
            ))}
        </ul>
    );
}

Sponsors

Project backers, we trust and recommend their services.


Logo Dark

Logo Light


Keycloak as a Service — Keycloak community contributors of popular extensions providing free and dedicated Keycloak hosting and enterprise Keycloak support to businesses of all sizes.




Logo Dark

Logo Light


Keycloak Consulting Services — Your partner in Keycloak deployment, configuration, and extension development for optimized identity management solutions.