Choose this when
Use this path when your Next.js App Router product owns the sign-in UI and wants Lamba to provide customer identity, scoped sessions, Project roles, and Customer API reads.
This guide uses the public customer hosts only:
https://test.id.uselamba.comorhttps://id.uselamba.comfor auth and session operationshttps://test.api.uselamba.comorhttps://api.uselamba.comfor Customer API reads
Before you start
You need:
- a Lamba Workspace
- a Lamba Project
- the selected environment, usually
testwhile integrating - a Next.js App Router app
- one server route for each auth action your UI exposes
Do not send console-only workspace, project, or environment headers from your product. Public customer requests are scoped by the bearer token and by POST /v1/sessions/switch-context.
Get credentials from Console
Open the Lamba console and use the top Workspace / Project selector first. Then open the selected Project and environment you are integrating.
| Value | Console source | Env var | Used for |
|---|---|---|---|
| Auth base URL | Choose Sandbox or Production from the console environment badge | LAMBA_CUSTOMER_AUTH_BASE_URL | Calls to `/v1/auth/*`, `/v1/sessions/switch-context`, and `/connect/*` |
| API base URL | Same selected environment as the auth base URL | LAMBA_CUSTOMER_API_BASE_URL | Calls to `/v1/me/*` and `/v1/admin/*` |
| Workspace ID | Top Workspace selector | LAMBA_CUSTOMER_WORKSPACE_ID | Optional immediate switch into one Workspace after login |
| Project ID | Top Project selector | LAMBA_CUSTOMER_PROJECT_ID | Optional immediate switch into one Project after login |
| App Client ID | Integration > App Clients | LAMBA_CLIENT_ID | Only needed if this Next.js app also performs OIDC hosted auth |
Configure environment variables
LAMBA_CUSTOMER_AUTH_BASE_URL=https://test.id.uselamba.com
LAMBA_CUSTOMER_API_BASE_URL=https://test.api.uselamba.com
LAMBA_CUSTOMER_WORKSPACE_ID=<workspace-id>
LAMBA_CUSTOMER_PROJECT_ID=<project-id>
LAMBA_CUSTOMER_ENV=test
LAMBA_SESSION_COOKIE=lamba_access_token
LAMBA_REFRESH_COOKIE=lamba_refresh_token
Use production hosts and production credentials only after the sandbox flow works end to end.
Make the first request
Start with password login if your Project has password auth enabled. Your browser posts to your own Next.js route. That route calls Lamba server-side and stores returned tokens in HttpOnly cookies.
https://test.id.uselamba.com/v1/auth/login/password- Auth
- None for the initial login request
- Used for
- Turns customer credentials into Lamba customer session tokens
// src/lib/lamba-customer.ts
import "server-only";
export async function customerAuthFetch(path: string, init?: RequestInit) {
return fetch(`${process.env.LAMBA_CUSTOMER_AUTH_BASE_URL}${path}`, {
cache: "no-store",
headers: { "content-type": "application/json", ...(init?.headers ?? {}) },
...init,
});
}
export async function customerApiFetch(path: string, accessToken: string) {
return fetch(`${process.env.LAMBA_CUSTOMER_API_BASE_URL}${path}`, {
cache: "no-store",
headers: { authorization: `Bearer ${accessToken}` },
});
}
// src/app/api/auth/login/password/route.ts
import { cookies } from "next/headers";
import { NextResponse } from "next/server";
import { customerAuthFetch } from "@/lib/lamba-customer";
export async function POST(request: Request) {
const payload = await request.json();
const response = await customerAuthFetch("/v1/auth/login/password", {
method: "POST",
body: JSON.stringify(payload),
});
const body = await response.json();
if (!response.ok) {
return NextResponse.json(body, { status: response.status });
}
const cookieStore = await cookies();
cookieStore.set("lamba_access_token", body.access_token, {
httpOnly: true,
secure: true,
sameSite: "lax",
path: "/",
});
cookieStore.set("lamba_refresh_token", body.refresh_token, {
httpOnly: true,
secure: true,
sameSite: "lax",
path: "/",
});
return NextResponse.json({ ok: true });
}
Request fields
For POST /v1/auth/login/password:
| Field | Type | Required | Meaning | Notes |
|---|---|---|---|---|
login | string | Required | Email, username, or configured login identifier entered by the customer. | Use `email` only if your UI intentionally accepts email-only login. |
password | string | Required | The customer password. | Send only from your server route or trusted native client flow. |
mfaCode | string | Conditional | One-time code when the login response requires MFA. | Submit after prompting for the configured factor. |
For POST /v1/sessions/switch-context after login:
| Field | Type | Required | Meaning | Notes |
|---|---|---|---|---|
workspaceId | string | Required | The Workspace selected in the Lamba console. | Customer-facing copy should call this Workspace, not tenant. |
projectId | string | Required | The Project that owns this product runtime. | Use the selected Project from the console top selector. |
environment | test | prod | Required | The environment whose auth, roles, domains, and webhooks should apply. | Keep sandbox and production sessions separate. |
Response fields
Login, refresh, and switch-context responses use the same token shape:
| Field | Type | Required | Meaning | Notes |
|---|---|---|---|---|
access_token | string | Required | Bearer token for the current customer session. | Store in an HttpOnly cookie for browser apps. |
refresh_token | string | Required | Credential used to rotate the customer session. | Treat like a password. Do not expose it to client props or localStorage. |
refresh_token_expires_at | ISO-8601 datetime | Required | When the refresh token stops being valid. | Restart login before or after this point instead of retrying forever. |
GET /v1/me/context is the simplest Customer API proof:
| Field | Type | Required | Meaning | Notes |
|---|---|---|---|---|
user | object | Required | The signed-in Lamba User profile visible to your product. | Includes values such as `id`, `email`, `username`, and `displayName`. |
activeWorkspace | object | null | Optional | Workspace context after switch-context. | Null means the session has not entered a Workspace yet. |
activeProject | object | null | Optional | Project and environment context after switch-context. | Use this to confirm your app is pinned to the expected Project. |
roleKeys | string[] | Required | Project runtime roles attached to the active session. | Use `/v1/me/authorization` for permission-level UI decisions. |
Read protected pages
After setting cookies, call the Customer API from server components or route handlers.
// src/app/app/page.tsx
import { cookies } from "next/headers";
import { redirect } from "next/navigation";
import { customerApiFetch } from "@/lib/lamba-customer";
export default async function AppPage() {
const cookieStore = await cookies();
const token = cookieStore.get("lamba_access_token")?.value;
if (!token) {
redirect("/login");
}
const response = await customerApiFetch("/v1/me/context", token);
if (!response.ok) {
redirect("/login");
}
const context = await response.json();
return <main>Signed in as {context.user.email}</main>;
}
Done when
Done when
- A customer can register or log in from your Next.js UI.
- Access and refresh tokens are stored only in HttpOnly cookies.
- Optional switch-context returns a scoped session for the expected Workspace, Project, and environment.
- `GET /v1/me/context` renders on a protected server page.
- Logout clears your cookies and calls Lamba logout when a token exists.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
Login succeeds but /v1/me/context is missing Project data | You did not call POST /v1/sessions/switch-context | Call switch-context with Workspace, Project, and environment after login |
401 from Customer API | Access token is missing, expired, or revoked | Refresh once, then restart login if refresh fails |
403 from an admin route | The user session lacks the required Project role | Read /v1/me/authorization and hide or block the action |
| Cookies exist but the browser still behaves as logged out | Cookies are not httpOnly, secure, sameSite=lax, or on the correct path | Set cookies from a server route and test over HTTPS outside localhost |
| Sandbox data appears in production | Test and production environment values were mixed | Use separate env files and separate App Clients per environment |
Related docs
- Hosted Auth Integration:
/docs/quickstart/hosted-auth - OIDC Integration:
/docs/quickstart/oidc - Customer API request and response contract:
/docs/quickstart/customer-api - Error Reference:
/docs/reference/errors