Next.js adapter
Mount login, callback, logout and back-channel logout in a Next.js app with one catch-all route, and what each route does.
On this page
The Next.js adapter turns the relying-party module into four route handlers. It is imported from the SDK's rp/next subpath. The handlers are plain functions of the standard Request and Response, so the underlying createAuthRoutes works in any runtime that speaks them; the adapter only dispatches by path.
This page is the reference for the handlers. The walkthrough is in the Quickstart and the behaviour you must build around them is in Log users in.
Mount#
Put one catch-all route file where appOrigin plus the path equals your registered redirect address.
import { createNextAuthHandlers } from '@tdacorp/identity-sdk/rp/next'const handlers = createNextAuthHandlers({ rp, appOrigin: 'https://app.example.com', cookieSecret, onLogin: async (login) => { // login.idToken.sub, login.tokens, login.returnTo, login.redirectTo return createSessionAndRedirect(login) },})export const GET = handlers.GETexport const POST = handlers.POSTcreateSessionAndRedirect is yours. handlers also exposes login, callback, logout and backchannelLogout for a file-per-route layout, for example export const GET = handlers.login.
Routes#
The adapter reads only the last path segment of the request, never the host.
| Path ends in | Method | What it does |
|---|---|---|
login | GET | Pushes the authorization request, seals the transaction into a cookie and redirects (302) to identity. Reads returnTo from the query. |
callback | GET | Checks the response, exchanges the code, verifies the ID token and calls onLogin. |
logout | POST (or GET with allowGet) | Clears your cookies and redirects (303) to identity's end-session address. Only when you pass logout. |
backchannel-logout | POST | Receives a logout token from identity. Only when you pass backchannelLogout. |
Anything else answers 404 with {"error":"not_found"}. In the catch-all route a wrong method on a known segment is also 404. A 405 with Allow comes only from a handler you mount on its own path.
Options#
| Option | Default | Meaning |
|---|---|---|
rp | required | A relying party. |
appOrigin | required | A bare origin. https, or http for loopback with secureCookies: false. |
cookieSecret | required | Seals the transaction cookie. At least 32 bytes, or a map for rotation. See Sealed sessions. |
onLogin | required | (login, request) => Response. Create your session here. |
onError | 400 JSON | Turns a failed callback into a response. Receives { code, message, cause }. |
scope | openid profile email | Must include openid. |
authorization | none | Extra request members: maxAge, acrValues, prompt, tenant, resource, loginHint, uiLocales. |
getTenant | none | (request) => tenantId or undefined: picks the organization for each sign-in, for an app whose people belong to different organizations. It wins over authorization.tenant; undefined falls back to it. A value that is not a canonical lowercase tenant id refuses the login with invalid_tenant. |
defaultReturnTo | / | Used when there is no valid returnTo. |
getReturnTo | the returnTo query | Read it from elsewhere. |
logout | route is 404 | { postLogoutRedirectUri, getIdTokenHint?, clearSessionCookies?, allowGet? } |
backchannelLogout | route is 404 | { onLogout, claimJti?, releaseJti? } |
transactionCookieName | __Host-tdaid_txn | The cookie base name. __Host- requires Secure. |
maxConcurrentLogins | 5 | Logins in flight, 1 to 10. Oldest are dropped. |
transactionTtlSeconds | 600 | How long a person has to finish. |
secureCookies | true | false only for an http loopback origin. |
What onLogin receives#
LoginSuccess: everything handleCallback returns (tokens, idToken), plus returnTo, the sanitized path or null, and redirectTo, an absolute address on appOrigin. Return a Response, normally a redirect to redirectTo that sets your own session cookie. The transaction cookie is expired for you on every outcome.
Errors#
A failed callback becomes a RouteError with one of these codes, passed to onError. The default response is 400 with { "error": code, "error_description": message }, or 500 for internal_error. The message is fixed and never repeats text from identity.
| Code | Cause |
|---|---|
invalid_tenant | getTenant returned something that is not a canonical tenant id. The login starts nothing; the default answer is 400. |
provider_error | Identity returned an error. |
missing_state, missing_transaction, invalid_transaction, state_mismatch | The browser's transaction is absent, expired, edited or does not match. |
invalid_response | A repeated parameter, a missing or wrong iss, or a missing code. |
token_exchange_failed | The token endpoint refused or could not be reached. |
token_verification_failed | The ID token failed a check. |
invalid_tenant | getTenant returned something that is not a canonical tenant id. Raised on login, before anything is sent to identity. |
internal_error | Anything else, including an exception from onLogin or getTenant. |
Back-channel logout#
The route accepts only application/x-www-form-urlencoded, a body of at most 8192 bytes and exactly one logout_token. It verifies the token, calls onLogout with sid, jti and the rest, and answers 200. A bad request is 400; an exception in your onLogout is 500, which lets identity retry. Replay protection defaults to an in-memory store per instance; pass claimJti and releaseJti over shared storage when you run more than one. See Back-channel logout.
Security notes#
- Absolute addresses come from
appOrigin. TheHostheader andX-Forwarded-*are never read. returnTomust be a path on your app. A protocol-relative address, a backslash, a control character or ajavascript:style value becomes the default.- Logout refuses a cross-origin request with
403, and refusesGETunless you setallowGet. - Without an ID token hint the logout route still redirects to identity, but identity then asks the person to confirm; see End session.