Overview
Use this guide to integrate Lamba in a Next.js App Router app with the customer surface:
- Embedded password, register, magic-link, or phone OTP auth
- HTTP-only session cookies
- Optional project and environment context switch
- Server-side customer API reads
- Logout
If you prefer redirect-based login, pair this guide with /docs/quickstart/oidc.
1) Install
npm install @lamba/sdk2) Environment variables
LAMBA_CUSTOMER_AUTH_BASE_URL=https://id.uselamba.com
LAMBA_CUSTOMER_API_BASE_URL=https://api.uselamba.com
LAMBA_CUSTOMER_WORKSPACE_ID=<workspace-id-optional>
LAMBA_CUSTOMER_PROJECT_ID=<project-id-optional>
LAMBA_CUSTOMER_ENV=test
LAMBA_SESSION_COOKIE=lamba_access_token
LAMBA_REFRESH_COOKIE=lamba_refresh_token
If you set LAMBA_CUSTOMER_WORKSPACE_ID and LAMBA_CUSTOMER_PROJECT_ID, switch-context runs immediately after login or register so later API calls stay pinned to that project and environment.
3) Reusable server helpers
// 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}` },
});
}
4) Auth route pattern
// 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 });
}
Use the same pattern for:
POST /v1/auth/registerPOST /v1/auth/login/magic-link/requestPOST /v1/auth/login/magic-link/verifyPOST /v1/auth/login/phone/requestPOST /v1/auth/login/phone/verifyPOST /v1/auth/email/verify
5) Optional switch-context
// call after login or register if your app pins one project
await customerAuthFetch("/v1/sessions/switch-context", {
method: "POST",
headers: { authorization: `Bearer ${accessToken}` },
body: JSON.stringify({
workspaceId: process.env.LAMBA_CUSTOMER_WORKSPACE_ID,
projectId: process.env.LAMBA_CUSTOMER_PROJECT_ID,
environment: process.env.LAMBA_CUSTOMER_ENV ?? "test",
}),
});
6) Protected page session check
// 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>;
}
7) Logout
// src/app/api/auth/logout/route.ts
import { cookies } from "next/headers";
import { NextResponse } from "next/server";
import { customerAuthFetch } from "@/lib/lamba-customer";
export async function POST() {
const cookieStore = await cookies();
const accessToken = cookieStore.get("lamba_access_token")?.value;
if (accessToken) {
await customerAuthFetch("/v1/auth/logout", {
method: "POST",
headers: { authorization: `Bearer ${accessToken}` },
});
}
cookieStore.delete("lamba_access_token");
cookieStore.delete("lamba_refresh_token");
return NextResponse.json({ ok: true });
}
Production checks
- Keep access and refresh tokens in
HttpOnlycookies. - Use
id.uselamba.comfor auth andapi.uselamba.comfor runtime and admin API calls. - Never send
X-Tenant-IdorX-Project-Idto the public customer surface. - Use
switch-contextwhen one app experience must stay pinned to a specific workspace, project, and environment. - Keep cookies
httpOnly,secure, andsameSite=lax.
Reference implementation
This repo also includes a fuller reference flow:
Clients/CustomerDemo- Next.js customer product demo using embedded auth, hosted OIDC, scoped cookies, and admin/runtime dashboard slices
Clients/WebhookSink- companion Next.js webhook receiver for customer event inspection
If you want a concrete sample instead of assembling the snippets above by hand, use those apps as the reference implementation for the customer surface contract.