### Initialize TanStack Start Example Source: https://docs.oidc-spa.dev/integration-guides/tanstack-router-start/tanstack-start.md Commands to clone the example repository, install dependencies, and start the development server. ```bash npx gitpick keycloakify/oidc-spa/tree/main/examples/tanstack-start start-oidc cd start-oidc npm install npm run dev # By default, the example runs against Keycloak. # You can edit the .env file to test other providers. ``` -------------------------------- ### Set up the Next.js example project Source: https://docs.oidc-spa.dev/integration-guides/next.js.md Clone the example Next.js App Router project, set up environment variables, install dependencies, and start the development server. The comment points to lib/oidc.tsx as the starting point for exploration. ```bash npx gitpick keycloakify/oidc-spa/tree/main/examples/next oidc-spa-next cd oidc-spa-next cp .env.local.sample .env.local npm install npm run dev # Start exploring with: lib/oidc.tsx ``` -------------------------------- ### Clone and run the Nuxt SPA example Source: https://docs.oidc-spa.dev/integration-guides/nuxt.md Commands to clone the example project, set up environment variables, install dependencies, and start the development server. The example uses the built-in `oidc-spa/nuxt-spa` support. ```bash npx gitpick keycloakify/oidc-spa/tree/main/examples/nuxt-spa oidc-spa-nuxt-spa cd oidc-spa-nuxt-spa cp .env.local.sample .env.local # Set your provider values in .env.local or enable mock mode yarn install yarn dev # Start exploring with: app/plugins/01.oidc.client.ts ``` -------------------------------- ### Clone and run TanStack Router file-based example Source: https://docs.oidc-spa.dev/integration-guides/tanstack-router-start/react-router.md Commands to clone the example project, set up environment, install dependencies, and run the dev server. ```bash npx gitpick keycloakify/oidc-spa/tree/main/examples/tanstack-router-file-router tr-oidc cd tr-oidc # You can use our preconfigured Keycloak, Auth0, or Google OAuth test accounts cp .env.local.sample .env.local npm install npm run dev # Start exploring with: src/oidc.ts ``` -------------------------------- ### Run the basic Angular example Source: https://docs.oidc-spa.dev/integration-guides/angular.md Scaffold and run the basic Angular example using gitpick. This example demonstrates the minimal setup for integrating oidc-spa with Angular. ```bash npx gitpick keycloakify/oidc-spa/tree/main/examples/angular oidc-spa-angular cd oidc-spa-angular npm install npm run start ``` -------------------------------- ### Initialize Framework Mode Example Source: https://docs.oidc-spa.dev/integration-guides/react-router.md Commands to clone and run the React Router Framework mode example. ```bash npx gitpick keycloakify/oidc-spa/tree/main/examples/react-router-framework rr-framework-oidc cd rr-framework-oidc # You can use our preconfigured Keycloak, Auth0, or Google OAuth test accounts cp .env.local.sample .env.local npm install npm run dev # Start exploring with: src/oidc.ts ``` -------------------------------- ### Get Access Token (Example) Source: https://docs.oidc-spa.dev/security-features/token-substitution.md Example of retrieving an access token. When Token Substitution is enabled, the returned token has a placeholder signature, so it cannot be used to call a resource server. ```typescript const accessToken = await oidc.getAccessToken(); ``` -------------------------------- ### Initialize Data Mode Example Source: https://docs.oidc-spa.dev/integration-guides/react-router.md Commands to clone and run the React Router Data mode example. ```bash npx gitpick keycloakify/oidc-spa/tree/main/examples/react-router-data rr-data-oidc cd rr-data-oidc # You can use our preconfigured Keycloak, Auth0, or Google OAuth test accounts cp .env.local.sample .env.local npm install npm run dev # Start exploring with: src/oidc.ts ``` -------------------------------- ### Manual setup: migrate token exfiltration defense config (src/main.ts) Source: https://docs.oidc-spa.dev/release-notes/v8-greater-than-v10.md For manual setup, replace `enableTokenExfiltrationDefense` and `resourceServersAllowedHostnames` with a `securityDefenses` object combining `browserRuntimeFreeze`, `DPoP`, and `tokenSubstitution`. ```diff import { oidcEarlyInit } from "oidc-spa/entrypoint"; +import { browserRuntimeFreeze } from 'oidc-spa/browser-runtime-freeze'; +import { DPoP } from 'oidc-spa/DPoP'; +import { tokenSubstitution } from 'oidc-spa/token-substitution'; const { shouldLoadApp } = oidcEarlyInit({ - enableTokenExfiltrationDefense: true, - resourceServersAllowedHostnames: ["s3.amazonaws.com"], + securityDefenses: { + ...browserRuntimeFreeze({ + //exclude: [ "fetch", "XMLHttpRequest", "Promise"] + }), + ...DPoP({ mode: 'auto' }), + ...tokenSubstitution({ + trustedExternalResourceServers: [ + "s3.amazonaws.com", + `*.${location.hostname.split('.').slice(-2).join('.')}`, + ], + }) + } }); ``` -------------------------------- ### Install oidc-spa and zod with bun Source: https://docs.oidc-spa.dev/integration-guides/nuxt.md Install the oidc-spa and zod packages using bun. ```bash bun add oidc-spa zod ``` -------------------------------- ### Manual setup - Create new entrypoint Source: https://docs.oidc-spa.dev/integration-guides/usage.md Create a new src/main.ts to handle early initialization and lazy loading of the main application. ```typescript import { oidcEarlyInit } from "oidc-spa/entrypoint"; const { shouldLoadApp } = oidcEarlyInit({ BASE_URL: "/" // The path where your app is hosted // If applicable you should use `process.env.PUBLIC_URL` // or `import.meta.env.BASE_URL`. // This is not an option. There's only one good answer. }); if (shouldLoadApp) { // Note: Deferring the main app import adds a few milliseconds to cold start, // but dramatically speeds up auth. Overall, it's a net win. import("./main.lazy"); } ``` -------------------------------- ### Initialize Declarative Mode Example Source: https://docs.oidc-spa.dev/integration-guides/react-router.md Commands to clone and run the React Router Declarative mode example. ```bash npx gitpick keycloakify/oidc-spa/tree/main/examples/react-router-declarative rr-declarative-oidc cd rr-declarative-oidc # You can use our preconfigured Keycloak, Auth0, or Google OAuth test accounts cp .env.local.sample .env.local npm install npm run dev # Start exploring with: src/oidc.ts ``` -------------------------------- ### Manual easy setup: oidcEarlyInit with Keycloak instance Source: https://docs.oidc-spa.dev/resources/migrating-from-keycloak-js.md Manual easy setup: call oidcEarlyInit in the same module where you create the Keycloak instance. This downgrades the security posture compared to the other approaches and may conflict with some client-side routing libraries. ```typescript import { Keycloak } from "oidc-spa/keycloak-js"; import { oidcEarlyInit } from "oidc-spa/entrypoint"; import { browserRuntimeFreeze } from 'oidc-spa/browser-runtime-freeze'; import { DPoP } from 'oidc-spa/DPoP'; // Should run as early as possible. oidcEarlyInit({ BASE_URL: "/" // The path where your app is hosted // If applicable you should use `process.env.PUBLIC_URL` // or `import.meta.env.BASE_URL`. // This is not an option. There's only one good answer. securityDefenses: { // See: https://docs.oidc-spa.dev/v/v10/security-features/browser-runtime-freeze ...browserRuntimeFreeze({ //excludes: [ "fetch", "XMLHttpRequest" ] }), // See: https://docs.oidc-spa.dev/v/v10/security-features/dpop ...DPoP({ mode: 'auto' }) } }); const keycloak = new Keycloak({ /* ... */ }); ``` -------------------------------- ### Manual recommended setup: rename entry and initialize oidcEarlyInit Source: https://docs.oidc-spa.dev/resources/migrating-from-keycloak-js.md Manual setup for a single client entrypoint: rename the entry file to main.lazy.ts, then create a new main.ts that calls oidcEarlyInit with security defenses and conditionally imports the lazy entry. The BASE_URL must be set to the app's hosted path. ```bash mv src/main.ts src/main.lazy.ts ``` ```typescript import { oidcEarlyInit } from "oidc-spa/entrypoint"; import { browserRuntimeFreeze } from 'oidc-spa/browser-runtime-freeze'; import { DPoP } from 'oidc-spa/DPoP'; const { shouldLoadApp } = oidcEarlyInit({ BASE_URL: "/" // The path where your app is hosted // If applicable you should use `process.env.PUBLIC_URL` // or `import.meta.env.BASE_URL`. // This is not an option. There's only one good answer. securityDefenses: { // See: https://docs.oidc-spa.dev/v/v10/security-features/browser-runtime-freeze ...browserRuntimeFreeze({ //excludes: [ "fetch", "XMLHttpRequest" ] }), // See: https://docs.oidc-spa.dev/v/v10/security-features/dpop ...DPoP({ mode: 'auto' }) } }); if( shouldLoadApp ){ import("./main.lazy"); } ``` -------------------------------- ### Run the advanced Angular example (angular-kitchensink) Source: https://docs.oidc-spa.dev/integration-guides/angular.md Scaffold and run the advanced Angular example (angular-kitchensink). It shows mocking the adapter, fetching initialization parameters remotely, protecting groups based on roles, validating the access token shape, and early rendering of public pages before OIDC initialization completes. ```bash npx gitpick keycloakify/oidc-spa/tree/main/examples/angular-kitchensink oidc-spa-angular-kitchensink cd oidc-spa-angular-kitchensink npm install npm run start ``` -------------------------------- ### Install oidc-spa and zod with pnpm Source: https://docs.oidc-spa.dev/integration-guides/nuxt.md Install the oidc-spa and zod packages using pnpm. ```bash pnpm add oidc-spa zod ``` -------------------------------- ### Install oidc-spa and zod with yarn Source: https://docs.oidc-spa.dev/integration-guides/nuxt.md Install the oidc-spa and zod packages using yarn. ```bash yarn add oidc-spa zod ``` -------------------------------- ### Query Documentation via HTTP GET Source: https://docs.oidc-spa.dev/integration-guides/backend-token-validation/deno.serve.md Perform an HTTP GET request to the documentation URL to retrieve answers or context regarding the Deno serve integration. ```http GET https://docs.oidc-spa.dev/integration-guides/backend-token-validation/deno.serve.md?ask=&goal= ``` -------------------------------- ### API Handler Implementation in src/main.ts Source: https://docs.oidc-spa.dev/integration-guides/backend-token-validation/node-http.md This example shows how to initialize authentication with bootstrapAuth and protect API routes using the getUser utility. It includes examples for basic user authentication and role-based access control. ```ts import { createServer } from "node:http"; import { parse } from "node:url"; import * as fs from "node:fs/promises"; import { bootstrapAuth, getUser } from "./auth"; // See below function startNodeServer() { bootstrapAuth({ implementation: "real", // or "mock", see: https://docs.oidc-spa.dev/v/v8/integration-guides/backend-token-validation/mock-modes issuerUri: process.env.OIDC_ISSUER_URI!, expectedAudience: process.env.OIDC_AUDIENCE }); const server = createServer(async (req, res) => { const { pathname } = parse(req.url!, true); if (req.method === "GET" && pathname === "/api/todos") { const user = await getUser({ req, res }); const json = await fs.readFile( `todos_${user.id}.json`, "utf8" ); res.writeHead(200, { "Content-Type": "application/json" }); res.end(json); return; } if ( req.method === "GET" && pathname?.startsWith("/api/todos-for-support/") ) { // Will reject the request if user making the request // doesn't have "support-staff" role await getUser({ req, res, requiredRole: "support-staff" }); const userId = decodeURIComponent( pathname.replace("/api/todos-for-support/", "") ); const json = await fs.readFile( `todos_${userId}.json`, "utf8" ); res.writeHead(200, { "Content-Type": "application/json" }); res.end(json); return; } res.writeHead(404).end(); }); server.listen(parseInt(process.env.PORT ?? "3000"), () => { console.log("Server running"); }); } ``` -------------------------------- ### CSP Configuration Example for Silent SSO Source: https://docs.oidc-spa.dev/resources/csp-configuration.md Example of a CSP header configuration that allows the IdP domain in frame-src and enables the app to frame itself via frame-ancestors. ```text Content-Security-Policy: frame-src https://auth.my-domain.com; frame-ancestors 'self'; ...other CSP directives... ``` -------------------------------- ### useOidc with fetch: Before and After (accessToken to tokens) Source: https://docs.oidc-spa.dev/release-notes/readme-1.md Two before and after examples showing how to replace oidcTokens.accessToken with the tokens object from useOidc when making authenticated API calls inside a useEffect. The after example shows that tokens will always be undefined on the first render, and a guard is needed. ```tsx const { oidcTokens } = useOidc({ assertUserLoggedIn: true }); useEffect(()=> { fetch( "https://...", { headers: { "Auhtorization": `Bearer ${oidcToken.accessToken}` }} ); }, []); ``` ```typescript const { tokens } = useOidc({ assert: "user logged in" }); useEffect(()=> { // Token will always be undefined the first render. if( tokens === undefined ) return; fetch( "https://...", { headers: { "Auhtorization": `Bearer ${tokens.accessToken}` }} ); }, []); ``` -------------------------------- ### Install oidc-spa and zod with npm, yarn, pnpm, or bun Source: https://docs.oidc-spa.dev/integration-guides/angular.md Install oidc-spa and the optional Zod validator using your preferred package manager. Zod is recommended for validating the shape of the access token in the advanced example. ```bash npm install oidc-spa zod ``` ```bash yarn add oidc-spa zod ``` ```bash pnpm add oidc-spa zod ``` ```bash bun add oidc-spa zod ``` -------------------------------- ### Manual setup - Easy approach Source: https://docs.oidc-spa.dev/integration-guides/usage.md Run oidcEarlyInit in the same module where you call createOidc. Note that this approach may downgrade security and conflict with some routing libraries. ```typescript import { oidcEarlyInit, createOidc } from "oidc-spa/core"; // Should run as early as possible. oidcEarlyInit({ BASE_URL: "/" // The path where your app is hosted // If applicable you should use `process.env.PUBLIC_URL` // or `import.meta.env.BASE_URL`. // This is not an option. There's only one good answer. }); const prOidc = createOidc({ /* ... See below ... */ }); export async function getOidc(){ const oidc = await prOidc; return oidc; } ``` -------------------------------- ### useOidc Hook Usage Before and After (decodedIdToken) Source: https://docs.oidc-spa.dev/release-notes/readme-1.md Before and after examples showing how to update the useOidc hook to get the decoded ID token. The after example uses the new assert option and destructures decodedIdToken directly instead of oidcTokens.decodedIdToken. If decodedIdToken is undefined the first render, handle it accordingly. ```tsx const { oidcTokens } = useOidc({ assertUserLoggedIn: true }); {oidcTokens.decodedIdToken.preffered_username} ``` ```tsx const { decodedIdToken } = useOidc({ assert: "user logged in" }); {decodedIdToken.preffered_username} ``` -------------------------------- ### React: bootstrapOidc configuration with access token validation Source: https://docs.oidc-spa.dev/providers-configuration/auth0.md Use bootstrapOidc in React to configure oidc-spa. Includes the same core settings as the framework-agnostic example, plus an additional .withAccessTokenValidation call for TanStack Start to validate access tokens with expected audience. ```typescript bootstrapOidc({ issuerUri: "auth.my-company.com", clientId: "DzXSmwQS7oSTQGLbafhrPXYLT0mOMyZD" extraQueryParams: { audience: "https://app.my-company.com/api" }, // Auth0 puts DPoP behind a paywall. Explicitely disable it until you have // enabled it in the Auth0 dashboard. disableDPoP: true, // (Optional) This must be kept in sync with the Idle Session Lifetime value // configured in the Auth0 dashboard. To ensure correct autoLogout behavior. idleSessionLifetimeInSeconds: 1800, // ... }); // In TanStack Start: .withAccessTokenValidation({ type: "RFC 9068: JSON Web Token (JWT) Profile for OAuth 2.0 Access Tokens", expectedAudience: () => "https://app.my-company.com/api", // ... }) ``` -------------------------------- ### Set up oidcEarlyInit in main.ts Source: https://docs.oidc-spa.dev/integration-guides/angular.md Rename your entry point to main.lazy.tsx and create a new main.ts that calls oidcEarlyInit() before any other JavaScript runs. This protects tokens against supply-chain attacks and XSS and improves login performance. ```bash mv src/main.tsx src/main.lazy.tsx ``` ```typescript import { oidcEarlyInit } from "oidc-spa/entrypoint"; const { shouldLoadApp } = oidcEarlyInit(); if (shouldLoadApp) { // Note: Deferring the main app import adds a few milliseconds to cold start, // but dramatically speeds up auth. Overall, it's a net win. import("./main.lazy"); } ``` -------------------------------- ### Query Documentation via HTTP GET Source: https://docs.oidc-spa.dev/integration-guides/backend-token-validation/hono.md Perform an HTTP GET request to the documentation URL to retrieve answers to specific questions. ```http GET https://docs.oidc-spa.dev/integration-guides/backend-token-validation/hono.md?ask=&goal= ``` -------------------------------- ### Query the documentation with ask and goal parameters Source: https://docs.oidc-spa.dev/integration-guides/angular.md Query this documentation dynamically by performing an HTTP GET request with the ask and optional goal query parameters. The response contains a direct answer and relevant excerpts from the documentation. ```http GET https://docs.oidc-spa.dev/integration-guides/angular.md?ask=&goal= ``` -------------------------------- ### Manual migration: update oidcEarlyInit and createOidc Source: https://docs.oidc-spa.dev/release-notes/v9-greater-than-v10.md Migration changes for manual setup in src/main.ts. Replace the extraDefenseHook with the securityDefenses option, importing browserRuntimeFreeze, DPoP, and tokenSubstitution. Remove the dpop property from createOidc or bootstrapOidc. ```diff import { oidcEarlyInit } from "oidc-spa/entrypoint"; -import { enableTokenSubstitution } from "oidc-spa/token-substitution"; +import { browserRuntimeFreeze } from 'oidc-spa/browser-runtime-freeze'; +import { DPoP } from 'oidc-spa/DPoP'; +import { tokenSubstitution } from 'oidc-spa/token-substitution'; const { shouldLoadApp } = oidcEarlyInit({ - browserRuntimeFreeze: { enabled: true }, - extraDefenseHook: () => { - enableTokenSubstitution({ - trustedThirdPartyResourceServers: ["s3.amazonaws.com"] - }); - } + securityDefenses: { + ...browserRuntimeFreeze({ + //excludes: [ "fetch", "XMLHttpRequest", "Promise"] + }), + ...DPoP({ mode: 'auto' }), + ...tokenSubstitution({ + trustedExternalResourceServers: [ + "s3.amazonaws.com", + `*.${location.hostname.split('.').slice(-2).join('.')}`, + ], + }), + } }); ``` ```diff createOidc({ // or bootstrapOidc({ // ... - dpop: "auto" }) ``` -------------------------------- ### Query documentation with ask parameter Source: https://docs.oidc-spa.dev/integration-guides/tanstack-router-start/react-router.md Use this HTTP GET request to ask a question about the documentation. The `ask` parameter is required and should be specific and self-contained; `goal` is optional for broader context. ```http GET https://docs.oidc-spa.dev/integration-guides/tanstack-router-start/react-router.md?ask=&goal= ``` -------------------------------- ### Update base URL for manual initialization Source: https://docs.oidc-spa.dev/release-notes/v8-greater-than-v9.md When using manual initialization, provide the base URL as a parameter to oidcEarlyInit. ```diff oidcEealyInit({ + BASE_URL: import.meta.env.BASE_URL }); ``` -------------------------------- ### Create new src/main.ts with oidcEarlyInit Source: https://docs.oidc-spa.dev/integration-guides/tanstack-router-start/react-router.md Creates a new entrypoint that calls oidcEarlyInit and conditionally imports the main app. Deferring the import speeds up auth. ```typescript import { oidcEarlyInit } from "oidc-spa/entrypoint"; const { shouldLoadApp } = oidcEarlyInit({ BASE_URL: "/" // The path where your app is hosted. You can also pass it later to createOidc(). }); if (shouldLoadApp) { // Note: Deferring the main app import adds a few milliseconds to cold start, // but dramatically speeds up auth. Overall, it's a net win. import("./main.lazy"); } ```