Launch offer: 50% off.Paid plans only.See pricing
Skip to content

Documentation

Quickstart

Next.js Quickstart

Quickstart v2: embedded customer auth, HttpOnly cookies, scoped sessions, and customer API reads.


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/sdk

2) 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/register
  • POST /v1/auth/login/magic-link/request
  • POST /v1/auth/login/magic-link/verify
  • POST /v1/auth/login/phone/request
  • POST /v1/auth/login/phone/verify
  • POST /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 HttpOnly cookies.
  • Use id.uselamba.com for auth and api.uselamba.com for runtime and admin API calls.
  • Never send X-Tenant-Id or X-Project-Id to the public customer surface.
  • Use switch-context when one app experience must stay pinned to a specific workspace, project, and environment.
  • Keep cookies httpOnly, secure, and sameSite=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.


Next

OIDC Quickstart
Connect Lamba as your OpenID Connect provider with a host-root issuer and branded domain support.