BFF Server Authentication
Authorization Code + PKCE is recommended for new server integrations. The browser receives only a 60-second one-time code; Authon tokens are returned only during the server-to-server exchange.
Recommended flow
Keep the PKCE verifier and state in an HttpOnly transaction cookie or server store. At the callback, validate state, exchange the code with a secret key, then create your application's own HttpOnly session.
2. Browser signs in to Authon in code mode
3. Authon returns a one-time code to the registered callback
4. Server exchanges the code using a secret key POST /v1/auth/token/exchange
5. Server creates its own HttpOnly session
import { createAuthonAuthorizationRequest } from '@authon/nextjs/server';
export async function POST() {
const { authorization } = await createAuthonAuthorizationRequest({
redirectUri: 'https:0
});
return Response.json(authorization, {
headers: { 'Cache-Control': 'no-store' },
});
}import { Authon } from '@authon/js';
const authorization = await fetch('/auth/start', { method: 'POST' })
.then((response) => response.json());
const authon = new Authon('pk_live_...', { sessionMode: 'bff' });
await authon.openSignIn(authorization);import { handleAuthonAuthorizationCallback } from '@authon/nextjs/server';
export async function GET(request: Request) {
const url = new URL(request.url);
const result = await handleAuthonAuthorizationCallback({
code: url.searchParams.get('code')!,
state: url.searchParams.get('state')!,
redirectUri: 'https:0
secretKey: process.env.AUTHON_SECRET_KEY!,
});
await createApplicationSession(result.user, result);
return Response.redirect(new URL('/app', url));
}Legacy SPA token mode
Token mode remains supported for existing SPAs, but new BFF integrations should use code mode above. Token mode exposes Authon tokens to browser JavaScript.
import { Authon } from '@authon/js';
const authon = new Authon('pk_live_...');
authon.on('signedIn', async (user) => {
const authonToken = authon.getToken();
// Send token to your backend
const res = await fetch('/api/auth/authon', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token: authonToken }),
});
const { serviceToken } = await res.json();
// Use serviceToken for your API calls
});
await authon.openSignIn();2. Backend: Verify token + issue session
Call Authon's token verification API from your backend to validate the token. If valid, create (or find) the user and issue your own service JWT.
Token Verification API
/v1/auth/token/verifyVerifies an Authon JWT and returns user information. No API key required.
Request
curl https://api.authon.dev/v1/auth/token/verify \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."Response
{
"valid": true,
"payload": {
"sub": "user-uuid",
"projectId": "project-uuid",
"type": "access",
"iat": 1711584000,
"exp": 1711584900
},
"user": {
"id": "user-uuid",
"email": "user@example.com",
"displayName": "Alan Kim",
"avatarUrl": null,
"emailVerified": true
}
}Always verify tokens server-side. Client-side token validation is insecure. Never issue service JWTs based on email alone — always verify the Authon token first.
getToken() Details
authon.getToken() returns the JWT issued by Authon. The token is signed with HS256 and expires after 15 minutes. The SDK automatically refreshes it when expired.
| Field | Value |
|---|---|
| Format | JWT (HS256) |
| Expiry | 15 minutes |
| Auto-refresh | Yes (handled by SDK) |
| Payload.sub | User UUID |
| Payload.projectId | Project UUID |