# Next.js (/docs/integrations/nextjs)

> Set up @better-iam/next in an App Router project, mount the IAM API, and read the session in server components, layouts, and client components.



`@better-iam/next` integrates Better IAM with the Next.js 15 App Router (React 19). The App Router spreads a
request across server components, layouts, route handlers, server actions, and middleware, and each exposes the
request differently: `headers()` and `cookies()` in components, a `Request` in route handlers, form data in
actions, and an edge runtime without database access in middleware. Calling the core API directly would mean
building the credential, redirecting with a safe `?next=`, mapping refusals to status codes, and writing session
cookies by hand in each of those places. One `createIamNext(iam)` call does it once and gives your server code:

* **Sessions in server components**: `getSession()` and `requireSession()`, memoized per request.
* **Guards**: `page()` for pages and layouts, `route()` and `apiRoute()` for route handlers, `action()` for server
  actions, and `require()` for one-off checks.
* **Rendering by permission**: `can()`, `allowed()`, and the `<iamNext.Can>` server component, batched into one
  `authorizeMany` per request.
* **A server-side typed client**: `client()` calls the IAM handler in process and writes the cookies it issues,
  so server actions can sign people in and out without client JavaScript.
* **Cross-site checks**: `route()`, `apiRoute()`, and `pages.api()` refuse cookie-authenticated mutations sent by
  another site's page ([details](/docs/integrations/nextjs/guards#cross-site-requests)). Next checks server
  actions itself.
* **Hydration**: `sessionForClient()` hands the session to `IamNextProvider`, so client components render the
  signed-in person on the first pass, matching the server's HTML ([Client components](#client-components)).
* **Auth forms**: drop-in server actions and unstyled, accessible forms for sign-in, MFA, step-up, password
  reset, sign-up, email verification, and invitations.
* **Organizations in the URL**, **edge middleware**, **step-up**, **service credentials**, **webhooks and
  assertions**, **background delivery**, and the **Pages Router**.

The package has three entries:

| Entry                     | Use from                                          | Contents                                                                                                                                           |
| ------------------------- | ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `@better-iam/next`        | Server components, route handlers, server actions | `createIamNext` and everything in the edge entry                                                                                                   |
| `@better-iam/next/edge`   | Middleware, edge route handlers, any Web runtime  | `createIamMiddleware`, `safeRedirectPath`, assertion and webhook verification. No Node, React, or database imports, so the edge bundle stays small |
| `@better-iam/next/client` | Client components (`'use client'`)                | `IamNextProvider`, `useSignOut`, the auth forms, and the React hooks                                                                               |

The umbrella package exposes the same entries as `better-iam/next`, `better-iam/next/edge`, and
`better-iam/next/client`.

  #### Every export, by entry

    **`@better-iam/next`** (also re-exports everything from the edge entry)

    | Export                                                                  | What it does                                                                                                 |
    | ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
    | `createIamNext(iam, options)`                                           | Creates the `iamNext` helpers described on these pages                                                       |
    | `checkStepUp(session, requirement, now?)`                               | Checks a session (or an `apiRoute` principal) against a step-up requirement; returns `null` or the failure   |
    | `isAuthenticationError(error)`                                          | True for the errors `getSession` treats as "signed out"                                                      |
    | `isNextControlError(error)`                                             | True for Next's `redirect()`, `notFound()`, `forbidden()`, and `unauthorized()` throws, which must propagate |
    | `parseSetCookie(header)`                                                | Parses a `Set-Cookie` header into the arguments `cookies().set` takes                                        |
    | `createAuthActions`, `authHiddenFields`, `authFields`, `authStepFields` | The building blocks behind `iamNext.authActions()` and the forms, for custom form containers                 |
    | `createBackground(resolveIam, options)`                                 | The building block behind `iamNext.background`, for instances you manage yourself                            |

    **`@better-iam/next/edge`**

    | Export                                                                                      | What it does                                                                                                                            |
    | ------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
    | `createIamMiddleware(options)`                                                              | Edge middleware: redirects visitors without a session cookie and forwards the path ([Middleware](/docs/integrations/nextjs/middleware)) |
    | `safeRedirectPath(value, fallback?)`                                                        | Returns a same-origin path, or the fallback, so `?next=` cannot become an open redirect                                                 |
    | `matchPath(pattern, pathname)`                                                              | The `*` / `**` glob matcher behind `publicPaths`                                                                                        |
    | `sessionCookieName(secure)`                                                                 | `__Host-better-iam.session` on HTTPS, `better-iam.session` otherwise                                                                    |
    | `pathnameHeader`                                                                            | The name of the forwarded-path header, `x-better-iam-pathname`                                                                          |
    | `verifyAssertionToken(token, options)`, `withAssertion(options, handler)`, `AssertionError` | Verify assertions offline in another service ([Assertions](/docs/integrations/nextjs/advanced#assertions-for-other-services))           |
    | `createWebhookHandler(options)`, `verifyWebhook(input)`                                     | Receive and verify your deployment's webhooks ([Webhooks](/docs/integrations/nextjs/advanced#receiving-your-own-webhooks))              |

    **`@better-iam/next/client`**

    | Export                                                                                                                                      | What it does                                                                                    |
    | ------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
    | `IamNextProvider`                                                                                                                           | `IamProvider` that refreshes server components when the signed-in identity changes              |
    | `useRouterSync()`                                                                                                                           | The hook inside `IamNextProvider`, for a provider tree you assemble yourself                    |
    | `useSignOut({ redirectTo? })`                                                                                                               | Returns a function that signs out, navigates, and refreshes server components                   |
    | `SignInForm`, `ReauthenticateForm`, `PasswordResetRequestForm`, `PasswordResetForm`, `SignUpForm`, `InvitationForm` and their `*View` twins | The auth forms ([Server actions and forms](/docs/integrations/nextjs/server-actions#the-forms)) |
    | `useSession`, `useAuthorize`, `useAccessible`, `useIamClient`, `Can`                                                                        | Re-exported from [`@better-iam/react`](/docs/integrations/react)                                |
  
## Application layout [#application-layout]

A complete App Router setup spreads over these files. The instance, the helpers, and the API route are the
minimum; the others add edge redirects, drop-in forms, and background delivery.

                    - `route.ts`
      
              - `route.ts`
      
              - `route.ts`
          
          - `layout.tsx`

      - `page.tsx`
    
          - `page.tsx`
    
    - `auth-actions.ts`

    - `layout.tsx`

    - `providers.tsx`
  
      - `iam.ts`

    - `iam-next.ts`
  
  - `middleware.ts`

  - `next.config.mjs`

| File                             | Holds                          | Runtime                                                         |
| -------------------------------- | ------------------------------ | --------------------------------------------------------------- |
| `lib/iam.ts`                     | `betterIam({ ... })`           | Node only (database, argon2)                                    |
| `lib/iam-next.ts`                | `createIamNext(iam, options)`  | Server components, actions, route handlers                      |
| `app/api/iam/[...path]/route.ts` | `iamNext.handlers()`           | The browser client, protocol mounts under `/api/iam`, `/health` |
| `middleware.ts`                  | `createIamMiddleware(...)`     | Edge: cookie presence and path forwarding                       |
| `app/auth-actions.ts`            | `iamNext.authActions()`        | Sign-in, step-up, and reset server actions                      |
| `app/api/webhooks/iam/route.ts`  | `createWebhookHandler(...)`    | Optional: consume your own IAM events                           |
| `app/api/cron/route.ts`          | `iamNext.background.cron(...)` | Optional: scheduled delivery and maintenance                    |

## Installation [#installation]

      ### Install the package [#install-the-package]

    <CodeBlockTabs defaultValue="npm" groupId="package-manager">
      <CodeBlockTabsList>
        <CodeBlockTabsTrigger value="npm">
          npm
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="pnpm">
          pnpm
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="yarn">
          yarn
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="bun">
          bun
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="npm">
        ```bash
        npm i @better-iam/next @better-iam/client @better-iam/server @better-iam/adapter-sqlite
        ```
      </CodeBlockTab>

      <CodeBlockTab value="pnpm">
        ```bash
        pnpm add @better-iam/next @better-iam/client @better-iam/server @better-iam/adapter-sqlite
        ```
      </CodeBlockTab>

      <CodeBlockTab value="yarn">
        ```bash
        yarn add @better-iam/next @better-iam/client @better-iam/server @better-iam/adapter-sqlite
        ```
      </CodeBlockTab>

      <CodeBlockTab value="bun">
        ```bash
        bun add @better-iam/next @better-iam/client @better-iam/server @better-iam/adapter-sqlite
        ```
      </CodeBlockTab>
    </CodeBlockTabs>

    Or install the umbrella package, `better-iam`, and import from its subpaths. See
    [Installation](/docs/installation) for other storage adapters.
  
      ### Create the instance [#create-the-instance]

    ```ts title="lib/iam.ts"
    import { betterIam } from '@better-iam/server';
    import { sqliteAdapter } from '@better-iam/adapter-sqlite';

    export const iam = betterIam({
      database: sqliteAdapter({ filename: process.env.BETTER_IAM_DATABASE ?? '.data/iam.db' }),
      secret: process.env.BETTER_IAM_SECRET!,
      baseURL: process.env.BETTER_IAM_BASE_URL ?? 'http://localhost:3000',
      permissions: { actions: ['documents:read', 'documents:write'] },
    });
    ```

    `baseURL` matters here: it becomes `iam.endpoint.origin`, the origin the in-process client presents and the one
    the server trusts for cookie requests.
  
      ### Create the Next.js helpers [#create-the-nextjs-helpers]

    `createIamNext` wraps the instance in the helpers your pages, route handlers, and actions use. `loginPath` and
    `stepUpPath` are where guards send people who must sign in or confirm who they are.

    ```ts title="lib/iam-next.ts"
    import { createIamNext } from '@better-iam/next';
    import { iam } from './iam';

    export const iamNext = createIamNext(iam, {
      loginPath: '/login',
      stepUpPath: '/reauth',
      interrupts: 'forbidden', // denials render app/forbidden.tsx
    });
    ```
  
      ### Mount the IAM HTTP API [#mount-the-iam-http-api]

    The browser client (including passkey ceremonies) reaches the server through this catch-all route. It answers with
    `iam.handler`, so it also serves protocol mounts whose paths fall under `/api/iam`. Mounts at other paths (SAML at
    `/saml` and SCIM at `/scim/v2` by default) need a route of their own, and the OAuth provider runs only on
    `iam.nodeHandler`; see [Protocol mounts](/docs/operations/deployment/protocol-mounts).

    ```ts title="app/api/iam/[...path]/route.ts"
    import { iamNext } from '@/lib/iam-next';

    export const runtime = 'nodejs';
    export const { GET, POST, OPTIONS } = iamNext.handlers();
    ```

    GET serves the operational `/health` and `/metrics` endpoints; every API method is a POST.
  
      ### Keep native modules out of the bundle [#keep-native-modules-out-of-the-bundle]

    The server and its native dependencies (`argon2`, `better-sqlite3`) must load as plain Node modules. List them in
    `serverExternalPackages`, or load the instance lazily and pass a factory instead of the instance.

          **serverExternalPackages:**

        ```js title="next.config.mjs"
        /** @type {import('next').NextConfig} */
        export default {
          serverExternalPackages: [
            '@better-iam/server',
            '@better-iam/auth',
            '@better-iam/adapter-sqlite',
            'argon2',
            'better-sqlite3',
          ],
          experimental: { authInterrupts: true }, // only for interrupts: true or 'forbidden'
        };
        ```
      
      **Lazy factory:**

        ```ts title="lib/iam.ts"
        import type { BetterIam } from '@better-iam/server';
        import { createIamNext } from '@better-iam/next';

        async function create(): Promise<BetterIam> {
          // Loaded at runtime from node_modules, never bundled.
          const [{ betterIam }, { sqliteAdapter }] = await Promise.all([
            import(/* webpackIgnore: true */ '@better-iam/server'),
            import(/* webpackIgnore: true */ '@better-iam/adapter-sqlite'),
          ]);
          const iam = betterIam({
            database: sqliteAdapter({ filename: process.env.BETTER_IAM_DATABASE ?? '.data/iam.db' }),
            secret: process.env.BETTER_IAM_SECRET!,
            baseURL: process.env.BETTER_IAM_BASE_URL ?? 'http://localhost:3000',
          });
          await iam.initialize();
          return iam;
        }

        // One instance per process: Next may evaluate this module once per server layer.
        const holder = globalThis as unknown as { __iam?: Promise<BetterIam> };
        export function getIam(): Promise<BetterIam> {
          holder.__iam ??= create();
          return holder.__iam;
        }

        export const iamNext = createIamNext(getIam, { loginPath: '/login' });
        ```
          
    `serverExternalPackages` only applies to packages resolved from `node_modules`. In a monorepo, workspace
    packages are symlinks outside it, so the example app and the console load the server with
    `import(/* webpackIgnore: true */ ...)` instead.
  
      ### Protect routes at the edge [#protect-routes-at-the-edge]

    Add [middleware](/docs/integrations/nextjs/middleware) that redirects visitors without a session cookie and
    forwards the requested path, so `?next=` fills itself.

    ```ts title="middleware.ts"
    import { NextResponse } from 'next/server';
    import { createIamMiddleware } from '@better-iam/next/edge';

    export const middleware = createIamMiddleware({
      loginPath: '/login',
      publicPaths: ['/', '/pricing', '/docs/**', '/invite/*'],
      next: (init) => NextResponse.next(init),
    });
    export const config = { matcher: ['/((?!_next|favicon.ico).*)'] };
    ```
  
## Protect routes [#protect-routes]

`iamNext.page(render, spec)` wraps a page or layout and `iamNext.route(handler, spec)` wraps a route handler. Each
requires a session, then an optional step-up, then an optional `authorize` rule, whose resource defaults to the
tenant itself (`iam/{tenantId}`) and whose tenant defaults to the session's.

```tsx title="app/projects/[id]/page.tsx"
import { iamNext } from '@/lib/iam-next';

export default iamNext.page(
  async (_props: { params: Promise<{ id: string }> }, { session, params }) => (
    <main>
      <h1>Project {params.id}</h1>
      <p>Signed in as {session.identity.email}</p>
    </main>
  ),
  {
    authorize: {
      action: 'projects:read',
      resource: ({ params }) => ({ type: 'project', id: params.id }),
    },
  },
);
```

[Guards](/docs/integrations/nextjs/guards) covers every guard and what each does on failure,
[Server actions and forms](/docs/integrations/nextjs/server-actions#guarded-mutations) covers guarded mutations, and
[Middleware](/docs/integrations/nextjs/middleware) covers edge redirects.

## Reading the session [#reading-the-session]

```tsx title="app/dashboard/page.tsx"
import { iamNext } from '@/lib/iam-next';

export default async function Dashboard() {
  const current = await iamNext.getSession();
  if (!current) return <a href="/login">Sign in</a>;
  return <h1>Welcome, {current.identity.name}</h1>;
}
```

`iamNext.getSession()` reads the request's cookies (through `headers()` and `cookies()`) and returns
`{ identity, session }`. It returns `null` whenever the server refuses the credential as a sign-in. That covers
missing, expired, or revoked sessions, sessions that must step up, unverified email addresses, inactive tenants,
and requests from a network the session or tenant does not allow. Any other failure is rethrown. `requireSession()` returns the session or redirects to the login
page with `?next=`; [Guards](/docs/integrations/nextjs/guards) covers it with the other wrappers.

* **Per-request memoization.** The no-argument call goes through React `cache`, so a layout, its page, and nested
  server components share one lookup per render. Calls that pass `headers` explicitly are not memoized. The
  `cache` option replaces React's; pass `(fn) => fn` to disable it.
* **After a server action.** The helper merges `cookies()` into the headers it sends. Cookies a server action
  just set, such as a new session after sign-in, therefore apply to the re-render Next performs in the same
  response.
* **Direct API calls.** `iamNext.credential()` returns `{ headers }` for any `iam.api.*` call made as the
  current visitor: `iam.api.identities.list(await iamNext.credential(), { tenantId })`.
* **No cross-request state.** The helpers never cache across requests. Memoization is scoped to one render, and
  there is no module-level session state.

### Client components [#client-components]

Client components get the session from the server so their first render needs no fetch.
`iamNext.sessionForClient()` returns the session as plain JSON without the stored token hash, or `null`. Pass it
to `IamNextProvider`, which is `IamProvider` plus a `router.refresh()` whenever the signed-in identity changes in
the browser ([details](/docs/integrations/nextjs/advanced#keeping-server-components-in-sync)).

  **app/layout.tsx:**

    ```tsx title="app/layout.tsx"
    import type { ReactNode } from 'react';
    import { iamNext } from '@/lib/iam-next';
    import { Providers } from './providers';

    export default async function RootLayout({ children }: { children: ReactNode }) {
      return (
        <html lang="en">
          <body>
            <Providers initialSession={await iamNext.sessionForClient()}>{children}</Providers>
          </body>
        </html>
      );
    }
    ```
  
  **app/providers.tsx:**

    ```tsx title="app/providers.tsx"
    'use client';
    import type { ReactNode } from 'react';
    import { createIamClient } from '@better-iam/client';
    import { IamNextProvider } from '@better-iam/next/client';
    import type { BetterIam } from '@better-iam/server';

    // Never called during server rendering (the provider starts from initialSession), but it needs an origin.
    const client = createIamClient<BetterIam>({
      baseURL: typeof window === 'undefined' ? 'http://localhost' : window.location.origin,
    });
    type Session = Awaited<ReturnType<BetterIam['api']['auth']['getSession']>>;

    export function Providers(props: { initialSession: Session | null; children: ReactNode }) {
      return (
        <IamNextProvider client={client} initialSession={props.initialSession}>
          {props.children}
        </IamNextProvider>
      );
    }
    ```
  
Inside the provider, the [React hooks](/docs/integrations/react) (`useSession`, `useAuthorize`, `useAccessible`,
`useIamClient`, `Can`) work as usual; `@better-iam/next/client` re-exports them.

## Organizations [#organizations]

Every session acts in one tenant (an organization), and guards default to it. For
applications that put the organization in the path (`/acme/projects`), `iamNext.requireTenantSession({ slug })`
resolves the alias with `tenants.lookup`, answers `notFound()` for unknown or inactive aliases, and sends visitors
signed in to another organization to `/login?org=...&next=...`. `iamNext.tenant(slug)` resolves an alias without
requiring a session. See [Organizations in the URL](/docs/integrations/nextjs/organizations) for the layout, the
pages below it, and the login page.

## Options [#options]

`createIamNext(iam, options)` takes these options. Most applications set `loginPath`, `stepUpPath`, and
`interrupts`; `headers`, `cookies`, `redirect`, and the other overrides exist for tests and custom setups.

<TypeTable
  type="{
  loginPath: {
    type: 'string',
    description: <>Where <code>requireSession</code>, <code>page</code>, and <code>requireTenantSession</code> send signed-out visitors, with <code>?next=</code>.</>,
    default: &#x22;'/login'&#x22;,
  },
  stepUpPath: {
    type: 'string',
    description: <>The re-authentication page for <code>stepUp</code> guards; it receives <code>?next=</code> and <code>?reason=</code>. Without it, page guards throw the step-up error. See <a href=&#x22;/docs/integrations/nextjs/advanced#step-up&#x22;>Step-up</a>.</>,
  },
  interrupts: {
    type: &#x22;boolean | 'forbidden'&#x22;,
    description: <>Use Next's <code>unauthorized()</code> and <code>forbidden()</code> (<code>experimental.authInterrupts</code>) instead of redirects or thrown errors. <code>'forbidden'</code> interrupts denials only.</>,
    default: 'false',
  },
  trustedOrigins: {
    type: 'string[]',
    description: <>Origins besides this application's own whose pages may send cookie-authenticated mutations to <code>route</code>, <code>apiRoute</code>, and <code>pages.api</code> handlers, for example a separate front end. The IAM origin is always trusted.</>,
  },
  dispatchAfterResponse: {
    type: 'boolean',
    description: <>Deliver queued email, SMS, webhooks, and events after each response with Next's <code>after()</code>. See <a href=&#x22;/docs/integrations/nextjs/advanced#background-work&#x22;>Background work</a>.</>,
    default: 'false',
  },
  background: {
    type: 'BackgroundOptions',
    description: <>Error reporting, batch size, and an <code>after()</code> override for background work.</>,
  },
  baseURL: {
    type: 'string',
    description: <>The IAM origin for <code>client()</code> when the instance does not report <code>endpoint</code>. Otherwise the origin comes from <code>x-forwarded-host</code> / <code>host</code>, and it must be one of the server's trusted origins.</>,
  },
  basePath: {
    type: 'string',
    description: <>The IAM mount path for <code>client()</code> when the instance does not report <code>endpoint</code>.</>,
    default: &#x22;'/api/iam'&#x22;,
  },
  cache: {
    type: '(fn) => fn',
    description: <>Memoizes the no-argument session read per request. Pass <code>(fn) =&gt; fn</code> to disable memoization.</>,
    default: &#x22;React's cache&#x22;,
  },
  headers: {
    type: '() => Headers | Promise' + '<' + 'Headers>',
    description: <>Reads the current request headers, which become the credential for every call. Override it only outside Next's request scope, for example in tests.</>,
    default: 'headers() merged with cookies()',
  },
  cookies: {
    type: '() => CookieStore | Promise' + '<' + 'CookieStore>',
    description: <>The mutable cookie store <code>client()</code> writes issued cookies to (anything with <code>set(name, value, options)</code>).</>,
    default: 'cookies() from next/headers',
  },
  redirect: {
    type: '(url: string) => never',
    description: <>Performs the login, step-up, and denial redirects; override it in tests.</>,
    default: 'redirect() from next/navigation',
  },
  unauthorized: {
    type: '() => never',
    description: <>Replaces Next's <code>unauthorized()</code>, which signed-out guards call when <code>interrupts</code> is <code>true</code>.</>,
    default: 'unauthorized() from next/navigation',
  },
  forbidden: {
    type: '() => never',
    description: <>Replaces Next's <code>forbidden()</code>, which denied guards call when <code>interrupts</code> is set.</>,
    default: 'forbidden() from next/navigation',
  },
  notFound: {
    type: '() => never',
    description: <>Replaces Next's <code>notFound()</code>, which <code>requireTenantSession</code> calls for an unknown organization alias.</>,
    default: 'notFound() from next/navigation',
  },
  now: {
    type: '() => number',
    description: <>The clock for step-up recency checks, for tests.</>,
    default: 'Date.now',
  },
}"
/>

`createIamNext` accepts the instance or a factory (`() => iam` or `() => Promise<iam>`). Any object with the
`IamLike` surface works; a custom one without `endpoint` passes `baseURL` and `basePath`.

## `iamNext` at a glance [#iamnext-at-a-glance]

Everything `createIamNext` returns, and where each member is explained:

| Member                                           | What it does                                                                               | Details                                                                             |
| ------------------------------------------------ | ------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------- |
| `getSession(headers?)`                           | The current `{ identity, session }`, or `null`; memoized per request                       | [Reading the session](#reading-the-session)                                         |
| `requireSession({ ... })`                        | The session, or a redirect to the login page (or the step-up page)                         | [Guards](/docs/integrations/nextjs/guards#lower-level-helpers)                      |
| `require({ tenantId, action, resource })`        | Enforces one action before rendering                                                       | [Guards](/docs/integrations/nextjs/guards#lower-level-helpers)                      |
| `page(render, spec)`                             | Wraps a page or layout: session, step-up, authorization, then render                       | [Guards](/docs/integrations/nextjs/guards#pages-and-layouts)                        |
| `route(handler, spec)`                           | Wraps a route handler for user sessions; answers IAM failures as JSON                      | [Guards](/docs/integrations/nextjs/guards#route-handlers)                           |
| `apiRoute(handler, spec)`                        | `route()` that also accepts API keys and assumed roles                                     | [Service credentials](/docs/integrations/nextjs/advanced#service-credentials)       |
| `action(fn, spec)`                               | Wraps a server action; returns `{ ok, data }` or `{ ok: false, error }`                    | [Server actions](/docs/integrations/nextjs/server-actions#guarded-mutations)        |
| `can({ tenantId, checks })`                      | Several advisory decisions in one call, keyed `action@type/id`                             | [Rendering by permission](/docs/integrations/nextjs/guards#rendering-by-permission) |
| `allowed(action, resource?)`, `<iamNext.Can>`    | One advisory decision, batched with every other check in the render                        | [Rendering by permission](/docs/integrations/nextjs/guards#rendering-by-permission) |
| `client()`                                       | The typed API, running in process, writing issued cookies through `cookies()`              | [In-process client](/docs/integrations/nextjs/server-actions#the-in-process-client) |
| `authActions(options)`                           | Drop-in server actions for sign-in, step-up, reset, sign-up, verification, and invitations | [Auth forms](/docs/integrations/nextjs/server-actions#auth-forms)                   |
| `handlers()`                                     | `GET`, `POST`, and `OPTIONS` for the `app/api/iam/[...path]` route                         | [Installation](#installation)                                                       |
| `sessionForClient()`                             | The session as plain JSON (no token hash) for `IamNextProvider`                            | [Client components](#client-components)                                             |
| `requireTenantSession({ slug })`, `tenant(slug)` | Resolve an organization alias and require a session in it                                  | [Organizations](/docs/integrations/nextjs/organizations)                            |
| `assertion({ tenantId, audience })`              | A short-lived signed assertion for a downstream service                                    | [Assertions](/docs/integrations/nextjs/advanced#assertions-for-other-services)      |
| `credential()`                                   | `{ headers }` for direct `iam.api.*` calls as the current visitor                          | [Reading the session](#reading-the-session)                                         |
| `currentPath()`                                  | The path the middleware forwarded for this request, when present and safe                  | [Middleware](/docs/integrations/nextjs/middleware)                                  |
| `clearSessionCookie()`                           | Expires the session cookie in the current response                                         | [Server actions](/docs/integrations/nextjs/server-actions#the-in-process-client)    |
| `background`                                     | Outbox and event dispatch: `dispatch()`, `schedule()`, and the `cron()` route              | [Background work](/docs/integrations/nextjs/advanced#background-work)               |
| `pages`                                          | Pages Router helpers: `handler`, `withSession`, `api`, `client`, `getSession`              | [Pages Router](/docs/integrations/nextjs/advanced#pages-router)                     |

## Related [#related]

  - [Guards](/docs/integrations/nextjs/guards): `page`, `requireSession`, `require`, `route`, and rendering by permission.

  - [Server actions and forms](/docs/integrations/nextjs/server-actions): The in-process client, guarded actions, and the drop-in auth forms.

  - [Organizations in the URL](/docs/integrations/nextjs/organizations): `requireTenantSession` for `/[org]/...` routes.

  - [Middleware](/docs/integrations/nextjs/middleware): Edge redirects, public paths, and `?next=` forwarding.

  - [Advanced](/docs/integrations/nextjs/advanced): Step-up, service credentials, webhooks, assertions, background work, and the Pages Router.

The [example app](https://github.com/better-iam/better-iam/tree/main/examples/nextjs) (`examples/nextjs`) runs
all of it on port 3300 with seeded accounts.
