
# Anmeldung in einer Next.js-App hinzufügen

Diese Schnellstartanleitung fügt einer Next.js-App mit App Router und dem SDK `@keenauth/nextjs` die KeenAuth-Anmeldung hinzu. Ihre App ist der OAuth-Client und hält alle Token auf dem Server; der Browser erhält nur ein verschlüsseltes Sitzungscookie mit HttpOnly-Flag. Sie fügen die Anmelde-Routen hinzu, schützen eine Seite, rufen eine geschützte API mit dem Access Token des Benutzers auf und testen die App offline. Der App Router ist der einzige Router, den das SDK unterstützt. Das SDK wird unter [Next.js SDK](/docs/sdk/nextjs) beschrieben.

Die App hat diese Seiten und Routen:

| Route | Funktion |
|---|---|
| `/` | Öffentliche Startseite: ein Anmeldelink oder der Name des Benutzers und eine Schaltfläche zum Abmelden |
| `/dashboard` | Erfordert eine Sitzung. Zeigt den Benutzer und die Nachrichten, die Ihre API zurückgibt |
| `/api/messages` | Erfordert eine Sitzung. Ruft Ihre API mit dem Access Token des Benutzers auf |
| `/auth/login`, `/auth/callback`, `/auth/logout` | Werden vom SDK bereitgestellt |

## Bevor Sie beginnen

- Eine KeenAuth-Umgebung. Ihr Issuer sieht etwa so aus: `https://your-environment.eu.keenauth.app`.
- Eine Web-Anwendung in der Umgebung ([Anwendung erstellen](/docs/api/management/applications/create)) mit `private_key_jwt` oder einem Client Secret als Client-Authentifizierung. In einer `development`-Umgebung registrieren Sie die Redirect-URI `http://localhost/auth/callback` und die Redirect-URI nach der Abmeldung `http://localhost/`. Eine Loopback-URI wird ohne Port registriert und passt auf jeden Port, etwa `3000`.
- Eine API, die in der Umgebung registriert ist ([API erstellen](/docs/api/management/apis/create)), mit dem Scope `messages:read`. Ihr `identifier`, zum Beispiel `https://api.example.com`, ist die Audience ihrer Access Token. Die [Express-Schnellstartanleitung](/docs/quickstarts/express) erstellt eine API, die diese Token akzeptiert.
- Node.js 22.18 oder neuer.

## 1. Installieren

```sh
npm install @keenauth/nextjs next@16.4.2 react@19.3.1 react-dom@19.3.1
```

Das SDK ist noch nicht veröffentlicht. Bis dahin verweist das Beispiel auf den SDK-Quellcode in dessen `package.json`.

## 2. Konfigurieren

Die App liest ihre Konfiguration ausschließlich aus Umgebungsvariablen. Legen Sie sie in `.env.local` ab, das Next.js lädt, und committen Sie diese Datei niemals.

| Variable | Erforderlich | Bedeutung |
|---|---|---|
| `KEENAUTH_ISSUER` | ja | Der Issuer Ihrer Umgebung |
| `KEENAUTH_CLIENT_ID` | ja | Die `client_id` der Web-Anwendung |
| `KEENAUTH_CLIENT_KEY` | eine der beiden | Der private Schlüssel (ein privater JSON-JWK) für `private_key_jwt`, empfohlen |
| `KEENAUTH_CLIENT_SECRET` | eine der beiden | Ein Client Secret |
| `APP_BASE_URL` | ja | Die öffentliche URL der App. Das SDK erstellt daraus die Redirect-URI und alle Weiterleitungen. `http` nur für `localhost`, `127.0.0.1` oder `[::1]` |
| `KEENAUTH_COOKIE_SECRET` | ja | Mindestens 32 Zeichen; versiegelt das Sitzungscookie. Erzeugen Sie einen Wert mit `openssl rand -base64 32` |
| `API_URL` | ja | Die API, die die App aufruft, zum Beispiel `http://localhost:8080` |
| `KEENAUTH_API_AUDIENCE` | ja | Der Identifier dieser API in KeenAuth |

`loadConfig` liest diese Werte und prüft sie. Die Funktion bricht ab, wenn einer davon fehlt oder wenn beide oder keine der Client-Zugangsdaten gesetzt sind. Ihre Fehlermeldungen enthalten nie ein Geheimnis.

```ts
export function loadConfig(env: Record<string, string | undefined>): AppConfig {
  const need = (name: string): string => {
    const value = env[name];
    if (!value) throw new Error(`${name} is required`);
    return value;
  };
```

## 3. SDK-Instanz erstellen

`createAuth` erstellt die SDK-Instanz, und das SDK prüft beim Erstellen jede Option. `getApp` erstellt beim ersten Aufruf je Prozess eine Instanz, sodass `next build` keine Geheimnisse benötigt.

```ts
/** Builds the SDK instance. The options are checked here, so a configuration error stops the app at start-up. */
export function createAuth(config: AppConfig, overrides: Partial<KeenAuthOptions> = {}): KeenAuth {
  return createKeenAuth({ ...config.auth, ...overrides });
}

let instance: { auth: KeenAuth; config: AppConfig } | undefined;

/** One instance per process, created on first use so that `next build` needs no secrets. */
export function getApp(): { auth: KeenAuth; config: AppConfig } {
  if (!instance) {
    const config = loadConfig(process.env);
    instance = { auth: createAuth(config), config };
  }
  return instance;
}
```

Die Konfiguration setzt `resource` auf den Identifier Ihrer API (RFC 8707). Ohne diesen Wert ist das Access Token nicht für Ihre API bestimmt, und die API lehnt es ab.

## 4. Anmelde-Routen hinzufügen

Ein einziger Catch-all-Route-Handler stellt `/auth/login`, `/auth/callback` und `/auth/logout` bereit:

```ts
// Serves /auth/login, /auth/callback and /auth/logout.
export const GET = (request: Request) => getAuth().handlers.GET(request);
export const POST = (request: Request) => getAuth().handlers.POST(request);
```

Die Anmeldung verwendet den Autorisierungscode-Flow mit PKCE und, sofern Ihre Umgebung es anbietet, eine hochgeladene Autorisierungsanfrage (Pushed Authorization Request). Der Callback prüft `state`, `iss` und das ID Token. Ein `returnTo`, das kein Pfad Ihrer App ist, wird ignoriert.

## 5. Seiten mit dem Proxy schützen

`proxy.ts` (vor Next.js 16 `middleware.ts`) erneuert die Sitzung, bevor eine Seite gerendert wird, und schützt die Pfade, die Sie auswählen. Eine Seitenanfrage ohne Sitzung wird mit `returnTo` zu `/auth/login` weitergeleitet. Eine Anfrage an `/api/…` erhält `401`.

```ts
export async function proxy(request: NextRequest) {
  const result = await getAuth().proxy(request, { protect: isProtected });
  if (result.response) return result.response;
  const response = NextResponse.next({ request: { headers: result.requestHeaders } });
  result.apply(response);
  return response;
}
```

Die Pfade, die eine Sitzung erfordern, stehen in einer einzigen Funktion:

```ts
export function isProtected(pathname: string): boolean {
  return pathname === "/dashboard" || pathname.startsWith("/dashboard/") || pathname.startsWith("/api/");
}
```

Ein Matcher des Proxys kann einen Pfad übersehen. Deshalb prüft jede Seite und jeder Route-Handler, der einen Benutzer benötigt, die Sitzung zusätzlich selbst.

## 6. Ihre API aufrufen

In einer Server Component lesen Sie die Sitzung und holen das Access Token aus dem Cookie-Store. Das Token bleibt auf dem Server; der Browser erhält nur das HTML.

```tsx
  // The proxy has already sent users without a session to the sign-in; check again here, because a matcher can miss a path.
  const session = await auth.getSession(cookieStore);
  if (!session) return <a href="/auth/login?returnTo=/dashboard">Sign in</a>;

  // The access token stays on the server. The browser receives only the HTML below.
  const response = await fetchMessages(config.apiUrl, await auth.getAccessToken(cookieStore));
```

`session.user` enthält die Claims des ID Token, etwa `subject`, `email` und `name`. Die Sitzung enthält nie ein Token.

Der eigentliche Aufruf ist ein einfaches `fetch` mit einem Bearer-Token:

```ts
export async function fetchMessages(apiUrl: string, accessToken: string, fetchImpl: typeof fetch = fetch): Promise<Response> {
  return fetchImpl(new URL("/api/messages", apiUrl), {
    headers: { Authorization: `Bearer ${accessToken}` },
    cache: "no-store",
  });
}
```

Ein Route-Handler kann auch Cookies setzen. Deshalb verwendet er `updateSession`, das ein Access Token erneuert, das bald abläuft:

```ts
  // A route handler can set cookies, so updateSession also refreshes an access token that is about to expire.
  const { accessToken, apply } = await auth.updateSession(request);
  if (!accessToken) return new Response(null, { status: 401 });
```

Geben Sie das Access Token nicht an eine Client Component weiter.

## 7. Abmelden

Die Abmeldung ist ein Formular, das an `/auth/logout` sendet. Das SDK löscht das Sitzungscookie, widerruft das Refresh Token und schickt den Browser zum Logout-Endpunkt Ihrer Umgebung, der ihn zurück zu Ihrer App leitet. Eine Anfrage, die der Browser als von einer anderen Website stammend kennzeichnet, wird abgelehnt.

```tsx
      <form action="/auth/logout" method="post">
        <button type="submit">Sign out</button>
      </form>
```

## 8. Die App ausführen

Starten Sie die API aus der Express-Schnellstartanleitung (oder eine andere API, die diese Token akzeptiert) und führen Sie die App aus:

```sh
npm run dev
```

Öffnen Sie `http://localhost:3000`, wählen Sie **Sign in**, melden Sie sich bei KeenAuth an und öffnen Sie das Dashboard.

## 9. Ohne Netzwerk testen

Die Tests benötigen kein Netzwerk und kein KeenAuth-Konto. Ein gefälschtes `fetch` übernimmt die Rolle des Autorisierungsservers (Discovery und die hochgeladene Autorisierungsanfrage), und Node führt die TypeScript-Tests direkt aus.

```ts
test("proxy: a page request without a session is redirected to sign-in with returnTo", async () => {
  const { auth } = app();
  const result = await auth.proxy(new Request(`${APP}/dashboard?tab=1`), { protect: isProtected });
  assert.equal(result.response?.status, 307);
```

```sh
npm test
npm run deps    # pinned versions, lockfile sources and licences
```

Die Tests prüfen, dass die Konfiguration beim Start validiert wird, dass eine Seite ohne Sitzung zur Anmeldung weiterleitet und ein API-Pfad mit `401` antwortet, dass die Anmeldung eine hochgeladene PKCE-S256-Anfrage sendet und ein versiegeltes HttpOnly-Cookie setzt, dass ein Callback mit unbekanntem State abgelehnt wird, dass eine Abmeldung von einer anderen Website abgelehnt wird und dass das Access Token im Header `Authorization` an die API übergeben wird.

Die vollständige Anmeldung mit einem echten Browser benötigt eine KeenAuth-Umgebung. Sie ist nicht Teil der Offline-Tests.

## Hinter einem Proxy oder Load Balancer

Setzen Sie `APP_BASE_URL` auf die öffentliche URL der App. Das SDK liest niemals `Host` oder `X-Forwarded-*`, um Weiterleitungen zu erstellen. Bei mehreren Server-Instanzen kann ein Refresh Token von zwei Instanzen gleichzeitig erneuert werden; siehe [Aktuelle Einschränkungen](/docs/sdk/nextjs#aktuelle-einschränkungen).

## Nächste Schritte

- [Next.js SDK](/docs/sdk/nextjs): alle Optionen, das Sitzungscookie und die aktuellen Einschränkungen.
- [Eine Express-API schützen](/docs/quickstarts/express): die API, die diese App aufruft.
- [Token prüfen](/docs/sdk/token-verification): der Verifier unter der Middleware Ihrer API.
