diff --git a/main/config/navigation/quickstarts.json b/main/config/navigation/quickstarts.json index c94c05b01a..b45a988344 100644 --- a/main/config/navigation/quickstarts.json +++ b/main/config/navigation/quickstarts.json @@ -18,6 +18,7 @@ "pages": [ "docs/quickstart/webapp/nextjs", "docs/quickstart/webapp/tanstack-start", + "docs/quickstart/webapp/react-router", "docs/quickstart/webapp/nuxt", "docs/quickstart/webapp/express", "docs/quickstart/webapp/express-beta", diff --git a/main/docs/images/icons/dark/react-router.svg b/main/docs/images/icons/dark/react-router.svg new file mode 100644 index 0000000000..911c8f6d8e --- /dev/null +++ b/main/docs/images/icons/dark/react-router.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/main/docs/images/icons/light/react-router.svg b/main/docs/images/icons/light/react-router.svg new file mode 100644 index 0000000000..30cee1849b --- /dev/null +++ b/main/docs/images/icons/light/react-router.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/main/docs/libraries.mdx b/main/docs/libraries.mdx index 042b41d5f0..faaf50d87a 100644 --- a/main/docs/libraries.mdx +++ b/main/docs/libraries.mdx @@ -26,6 +26,7 @@ Have a traditional web application that runs on a server? Auth0 maintains these + diff --git a/main/docs/quickstart/webapp/react-router.mdx b/main/docs/quickstart/webapp/react-router.mdx new file mode 100644 index 0000000000..52afad9077 --- /dev/null +++ b/main/docs/quickstart/webapp/react-router.mdx @@ -0,0 +1,428 @@ +--- +title: Add Login to Your React Router Application +sidebarTitle: React Router +description: This guide demonstrates how to integrate Auth0 with a React Router application (framework mode, v7 or later) using the Auth0 React Router SDK. +mode: wide +validatedOn: 2026-09-17 +--- + +import {AuthCodeBlock} from "/snippets/AuthCodeBlock.jsx"; +import {CreateInteractiveApp} from "/snippets/recipe.jsx"; +import {HowToSchema} from "/snippets/HowToSchema.jsx"; + + + + + `@auth0/auth0-react-router` is currently in **beta** (`1.0.0-beta.2`). The API may change before the stable 1.0 release. + + + +If you use an AI coding assistant like Claude Code, Cursor, or GitHub Copilot, you can add Auth0 authentication automatically in minutes using [agent skills](https://agentskills.io/home). + +**Install:** + +```bash +npx skills add auth0/agent-skills --skill auth0 +``` + +**Then ask your AI assistant:** + +```text +Add Auth0 authentication to my React Router app +``` + +Your AI assistant will automatically create your Auth0 application, fetch credentials, install `@auth0/auth0-react-router`, configure the provider, and set up your routes. [Full agent skills documentation →](/docs/quickstart/agent-skills) + + + + **Prerequisites:** Before you begin, ensure you have the following installed: + + - **[Node.js](https://nodejs.org/en/download)** 18 or newer (20 LTS recommended) + - **[npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm)** 9+, **[yarn](https://classic.yarnpkg.com/lang/en/docs/install/)** 1.22+, or **[pnpm](https://pnpm.io/installation)** 8+ + - **[jq](https://jqlang.org/)** - Required for Auth0 CLI setup + - React Router framework mode, v7 or later (`react-router.config.ts` present) + + + +## Get Started + +This quickstart demonstrates how to add Auth0 authentication to a React Router application. You'll build a secure app with login, logout, and user profile features using the Auth0 React Router SDK. The SDK handles the OIDC flow server-side and stores the session in a JWE-encrypted cookie — tokens never reach the browser. + +export function generateRandomString(length) { + const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + return Array.from({length}, () => chars[Math.floor(Math.random() * chars.length)]).join(''); +} + +export const envSnippet = `AUTH0_DOMAIN={yourDomain} +AUTH0_CLIENT_ID={yourClientId} +AUTH0_CLIENT_SECRET={yourClientSecret} +AUTH0_SESSION_SECRET=${generateRandomString(32)} +AUTH0_APP_BASE_URL=http://localhost:5173`; + + + + Create a new React Router project for this Quickstart: + + ```shellscript + npx create-react-router@latest my-app + ``` + + Open the project: + + ```shellscript + cd my-app && npm install + ``` + + + Skip this step if you are adding Auth0 to an existing React Router app. + + + + + ```shellscript + npm install @auth0/auth0-react-router + ``` + + + + Create an Auth0 application and set the callback and logout URLs. + + + + + + + + Run the following command from your project's root directory to create an Auth0 app and generate a `.env` file: + + + ```shellscript Mac + # Install Auth0 CLI (if not already installed) + brew tap auth0/auth0-cli && brew install auth0 + + # Set up Auth0 app and generate .env file + auth0 qs setup --app --type regular --framework react-router --port 5173 --name "My React Router App" + ``` + + ```powershell Windows + # Install Auth0 CLI (if not already installed) + scoop bucket add auth0 https://github.com/auth0/scoop-auth0-cli.git + scoop install auth0 + + # Set up Auth0 app and generate .env file + auth0 qs setup --app --type regular --framework react-router --port 5173 --name "My React Router App" + ``` + + + + This command will create an Auth0 Regular Web Application configured for `http://localhost:5173` and generate a `.env` file with all required credentials. Skip step 4 if you use this option. + + + + + 1. Go to [Auth0 Dashboard](https://manage.auth0.com/) → **Applications > Applications** + 2. Click **Create Application**, enter a name, and select **Regular Web Application** + 3. Click **Create** + 4. Open the **Settings** tab (Application Settings) + 5. Set the following fields: + + | Field | Value | + |-------|-------| + | Allowed Callback URLs | `http://localhost:5173/auth/callback` | + | Allowed Logout URLs | `http://localhost:5173` | + + 6. Click **Save Changes** + 7. Copy **Domain** and **Client ID** from Application Settings — you will use them in the next step + + + + + + Create a `.env` file at the root of your project: + + + + | Variable | Where to find it | + |----------|-----------------| + | `AUTH0_DOMAIN` | Application Settings → Domain | + | `AUTH0_CLIENT_ID` | Application Settings → Client ID | + | `AUTH0_CLIENT_SECRET` | Application Settings → Client Secret | + | `AUTH0_SESSION_SECRET` | A random 32+ character string — auto-generated in the snippet above | + | `AUTH0_APP_BASE_URL` | Your app's base URL — `http://localhost:5173` for local development | + + + Never commit `.env` to version control. Add it to `.gitignore` before your first commit. + + + + + Create `app/auth0.server.ts`. The `.server.ts` suffix tells React Router's bundler to exclude this file from the client bundle, keeping your secrets server-only. + + ```ts app/auth0.server.ts + import { Auth0Server, registerAuth0Instance } from '@auth0/auth0-react-router/server'; + + export const auth0 = new Auth0Server(); + registerAuth0Instance(auth0); + ``` + + + + Create a splat route that handles all `/auth/*` paths. `handleAuth` dispatches internally to `handleLogin`, `handleCallback`, `handleLogout`, and `handleBackchannelLogout` based on the URL path and HTTP method. + + ```tsx app/routes/auth.$.tsx + import { handleAuth } from '@auth0/auth0-react-router/server'; + import { auth0 } from '../auth0.server'; + + export const loader = ({ request }: { request: Request }) => + handleAuth(auth0, request); + + export const action = ({ request }: { request: Request }) => + handleAuth(auth0, request); + ``` + + Register the route in your route config: + + ```ts app/routes.ts + import { type RouteConfig, route } from '@react-router/dev/routes'; + + export default [ + route('auth/*', 'routes/auth.$.tsx'), + // ... your other routes + ] satisfies RouteConfig; + ``` + + + + Add `Auth0Provider` and `rootAuthLoader` to `app/root.tsx`. `rootAuthLoader` decrypts the session cookie and passes the auth state to the provider — no tokens are sent to the browser. + + ```tsx app/root.tsx expandable + import { Links, Meta, Outlet, Scripts, ScrollRestoration } from 'react-router'; + import { Auth0Provider } from '@auth0/auth0-react-router'; + import { rootAuthLoader } from '@auth0/auth0-react-router/server'; + import type { Route } from './+types/root'; + + export const loader = ({ request }: Route.LoaderArgs) => + rootAuthLoader(request); + + export default function Root() { + return ( + + + + + + + + + + + + + + + + ); + } + ``` + + + `Auth0Provider` reads session data from `useRouteLoaderData('root')`, so the root route must have the id `root`. With file-based routing React Router sets this from the filename automatically. With a custom route config, pass `{ id: 'root' }` to the `layout()` call. + + + + + Use the built-in components to show login and logout controls. `LoginButton` redirects to `/auth/login` and `LogoutButton` redirects to `/auth/logout`. Auth0 handles the OIDC flow and redirects the user back to your app after sign-in. + + ```tsx app/routes/_index.tsx + import { + AuthLoading, + LoginButton, + LogoutButton, + SignedIn, + SignedOut, + } from '@auth0/auth0-react-router'; + + export default function Home() { + return ( +
+

Welcome

+ +

Loading…

+
+ + Log in + + + Log out + +
+ ); + } + ``` +
+ + + Use the `useUser` hook to access the authenticated user's profile in any client component. Pair it with `requireSession` in the loader to block unauthenticated requests at the server before the page renders. + + ```tsx app/routes/profile.tsx + import { useUser } from '@auth0/auth0-react-router'; + import { requireSession } from '@auth0/auth0-react-router/server'; + import type { Route } from './+types/profile'; + + export const loader = async ({ request }: Route.LoaderArgs) => { + await requireSession(request); + return null; + }; + + export default function Profile() { + const user = useUser(); + + return ( +
+

Profile

+ {user && ( +
+ {user.name +

{user.name}

+

{user.email}

+
+ )} +
+ ); + } + ``` +
+ + + ```shellscript + npm run dev + ``` + + Open `http://localhost:5173` in your browser and click **Log in**. You will be redirected to the Auth0 Universal Login page. After signing in you will be redirected back to your app. + +
+ + + Your app now has working login and logout. The session is stored in a JWE-encrypted cookie — access tokens stay on the server and are never sent to the browser. + + +## Troubleshooting + + + + + +**Cause:** `AUTH0_SESSION_SECRET` changed after a session cookie was issued, or the value is fewer than 32 characters. + +**Fix:** Clear your browser cookies for `localhost`, confirm `AUTH0_SESSION_SECRET` is at least 32 characters, and restart the dev server. To generate a new secret: `openssl rand -hex 32`. + + + + + +**Cause:** The redirect URL Auth0 receives does not match any value in Allowed Callback URLs. + +**Fix:** In [Auth0 Dashboard](https://manage.auth0.com/) → **Applications > Applications** → select your app → **Application Settings**, confirm **Allowed Callback URLs** is set to `http://localhost:5173/auth/callback`. Remove any trailing slashes or extra whitespace, then click **Save Changes**. + + + + + +**Cause:** The `auth.$.tsx` splat route is missing or not registered in `routes.ts`. + +**Fix:** Confirm `app/routes/auth.$.tsx` exists and that `app/routes.ts` includes `route('auth/*', 'routes/auth.$.tsx')`. Restart the dev server after editing `routes.ts`. + + + + + +**Cause:** `rootAuthLoader` is not exported from `app/root.tsx`, or the root route does not have the id `root`. + +**Fix:** Confirm `app/root.tsx` exports `export const loader = ({ request }) => rootAuthLoader(request)`. With a custom route config, register the root layout as `layout('root.tsx', { id: 'root' }, [...routes])`. + + + + + +**Cause:** `defineRouteAuth` and `auth0Middleware` require React Router 7.9.0 or later, which introduced the middleware API. + +**Fix:** Upgrade `react-router` to `>=7.9.0`, or protect routes individually using `requireSession` / `requireUser` in each loader instead. + + + + + +## Advanced Usage + + + + + +Add `AUTH0_AUDIENCE` to `.env` with your API's identifier (from [Auth0 Dashboard](https://manage.auth0.com/) → **Applications > APIs** → **API Settings → Identifier**). Then use `getAccessToken` in a loader — the token never reaches the browser: + +```ts app/routes/data.tsx +import { getAccessToken } from '@auth0/auth0-react-router/server'; +import { TokenError } from '@auth0/auth0-react-router/errors'; + +export const loader = async ({ request }) => { + let token: string; + try { + token = await getAccessToken(request); + } catch (err) { + if (err instanceof TokenError) { + return new Response(null, { status: 302, headers: { Location: '/auth/login' } }); + } + throw err; + } + const data = await fetch('https://api.example.com/items', { + headers: { Authorization: `Bearer ${token}` }, + }).then(r => r.json()); + return { data }; +}; +``` + + + + + +Use `defineRouteAuth` middleware (React Router ≥ 7.9.0) to enforce roles at the route level. Roles are read from the `https://auth0.com/claims/roles` claim by default: + +```ts app/routes/admin.tsx +import { defineRouteHandle } from '@auth0/auth0-react-router'; +import { defineRouteAuth, auth0UserContext } from '@auth0/auth0-react-router/server'; + +export const handle = defineRouteHandle({ role: 'admin' }); + +export const middleware = defineRouteAuth({ role: 'admin' }).middleware; + +export const loader = ({ context }) => { + const user = context.get(auth0UserContext); + return { user }; +}; +``` + +Requests without the required role receive a `403`. + + + + + +The SDK can run in a purely client-side mode backed by `@auth0/auth0-spa-js`. Add `VITE_AUTH0_DOMAIN` and `VITE_AUTH0_CLIENT_ID` to your `.env` — `Auth0Provider` detects these automatically and switches to the PKCE flow. No other code changes are required. + +```shellscript +# .env additions for SPA mode +VITE_AUTH0_DOMAIN={yourDomain} +VITE_AUTH0_CLIENT_ID={yourClientId} +``` + + +Do not set both `AUTH0_*` and `VITE_AUTH0_*` variables at the same time. Hybrid mode is not supported — when both are present, SPA logout will not clear the server-side session cookie. + + + + + diff --git a/main/docs/quickstarts.mdx b/main/docs/quickstarts.mdx index 7b1ec2482a..752427a8bc 100644 --- a/main/docs/quickstarts.mdx +++ b/main/docs/quickstarts.mdx @@ -178,6 +178,17 @@ Traditional web app that runs on the server }} /> + +