Docs/Cookbook: Next.js Integration

Cookbook: Next.js Integration

Practical guidance for integrating clients and enforcing secure access.

Cookbook: Integrate Fulan Auth in a Next.js App

This cookbook walks through a simple, production-friendly pattern for integrating a Next.js app with the Fulan Auth Service using Authorization Code + PKCE.

What you will build

  • A "Sign in" button that redirects to /authorize
  • A callback route that exchanges code for tokens via /token
  • A server-side session using HttpOnly cookies
  • A protected page that requires a valid access token

Prerequisites

  • A registered app in the Portal
  • You have client_id
  • You have an allowed redirect_uri (exact match)
  • Your scope allowlist includes what you need (example: openid)
  • Auth Service base URL (examples assume http://localhost:8080)

Step 1: Add environment variables

Add these to your Next.js environment (example names):

NameExample
AUTH_BASE_URLhttp://localhost:8080
FULAN_CLIENT_IDyour-client-id
FULAN_REDIRECT_URIhttp://localhost:3000/auth/callback
FULAN_SCOPESopenid

Step 2: Create PKCE helper utilities

You need a code_verifier and a code_challenge:

  • code_verifier: random string, stored temporarily for the login attempt
  • code_challenge: base64url(SHA256(code_verifier))

Example helper (Node/Next.js runtime):

import crypto from "node:crypto";

function base64url(input: Buffer) {
  return input
    .toString("base64")
    .replaceAll("+", "-")
    .replaceAll("/", "_")
    .replaceAll("=", "");
}

export function createPKCE() {
  const verifier = base64url(crypto.randomBytes(32));
  const challenge = base64url(crypto.createHash("sha256").update(verifier).digest());
  return { verifier, challenge };
}

Step 3: Add a login route that redirects to /authorize

Create an endpoint that:

  1. Generates PKCE values
  2. Generates a random state
  3. Stores code_verifier and state in HttpOnly cookies (short-lived)
  4. Redirects the browser to the Auth Service /authorize URL

Authorize URL parameters:

  • response_type=code
  • client_id
  • redirect_uri (exact match)
  • scope
  • code_challenge
  • code_challenge_method=S256
  • state

Example (route handler pseudo-code):

const authBase = process.env.AUTH_BASE_URL!;
const clientId = process.env.FULAN_CLIENT_ID!;
const redirectUri = process.env.FULAN_REDIRECT_URI!;
const scopes = process.env.FULAN_SCOPES ?? "openid";

const { verifier, challenge } = createPKCE();
const state = crypto.randomUUID();

const authorizeURL = new URL("/authorize", authBase);
authorizeURL.searchParams.set("response_type", "code");
authorizeURL.searchParams.set("client_id", clientId);
authorizeURL.searchParams.set("redirect_uri", redirectUri);
authorizeURL.searchParams.set("scope", scopes);
authorizeURL.searchParams.set("code_challenge", challenge);
authorizeURL.searchParams.set("code_challenge_method", "S256");
authorizeURL.searchParams.set("state", state);

return redirect(authorizeURL.toString());

Cookie guidance:

  • Store pkce_verifier and oauth_state as HttpOnly cookies
  • Use short expiration (5–10 minutes)
  • Set secure=true in production

Step 4: Add a callback route to exchange code for tokens

The Auth Service redirects back to:

<redirect_uri>?code=<auth_code>&state=<state>

In the callback handler:

  1. Read code and state from the query string
  2. Read pkce_verifier and the expected oauth_state from cookies
  3. Validate state matches (CSRF protection)
  4. Call /token with application/x-www-form-urlencoded
  5. Store tokens in secure cookies (HttpOnly)
  6. Redirect to the app

Token exchange request:

POST /token
content-type: application/x-www-form-urlencoded

grant_type=authorization_code
client_id=<client_id>
code=<auth_code>
redirect_uri=<redirect_uri>
code_verifier=<code_verifier>

Step 5: Create a session cookie model (recommended)

For most Next.js apps:

  • Store access_token in an HttpOnly cookie
  • Optionally store refresh_token in an HttpOnly cookie
  • Do not store tokens in localStorage

On each request (server side), read the cookie and:

  • Validate the JWT using JWKS
  • Enforce scopes from the scope claim

Step 6: Protect pages or routes

Common patterns:

  • Middleware redirects unauthenticated users to /auth/login
  • Server Components check for the cookie and redirect if missing

Minimum check:

  • If no access_token, redirect to login

Stronger check:

  • Verify JWT signature via JWKS
  • Validate iss, aud, exp
  • Validate scope

Step 7: Logout

Logout can be a simple cookie clear:

  1. Clear access_token and refresh_token cookies
  2. Clear PKCE/state cookies if any remain
  3. Redirect to home

Troubleshooting

  • Redirect error: redirect_uri must match exactly what you registered.
  • Token exchange fails: confirm code_verifier is the same one that produced the code_challenge.
  • Scope rejected: requested scopes must exist in the app allowlist.
  • State mismatch: ensure cookies are set correctly and not blocked by cross-site policies.