KeyloomKeyloom
Keyloom Auth is currently in beta. Feedback and contributions are welcome!
Server

Server Endpoints

REST endpoints exposed by Keyloom handlers and their request and response formats.

Server Endpoints

Keyloom exposes a standard set of endpoints. Paths assume the base route /api/auth.

GET /api/auth/session

  • Purpose: fetch the current session
  • Response: 200 with session JSON or 204 when not authenticated
{
  "user": { "id": "u_123", "name": "Jane" },
  "expiresAt": "2025-01-01T12:00:00.000Z"
}

POST /api/auth/signin/:provider

  • Purpose: start OAuth flow for a provider
  • Request: JSON body optional for custom params
  • Response: redirect to provider or 400 on invalid provider

GET /api/auth/callback/:provider

  • Purpose: finish OAuth flow and create a session
  • Response: redirect to your app, sets cookie
  • Errors: OAUTH_CALLBACK_ERROR with details in logs

POST /api/auth/logout

  • Purpose: destroy session
  • Response: 204 and clears cookie

GET /api/auth/jwks

  • Purpose: JWKS for JWT verification when using asymmetric signing
  • Response: JWKS JSON

Status codes

  • 200 - OK
  • 204 - No content
  • 302 - Redirect during OAuth
  • 400 - Bad request
  • 401 - Unauthorized
  • 500 - Internal error

Error shape

{
  "code": "OAUTH_CALLBACK_ERROR",
  "message": "Provider returned an error"
}

See Next.js Handler for implementation specifics and examples.


Prerequisites

  • keyloom.config.ts configured and wired to your Next.js handler
  • Public base URL set (for OAuth callbacks)
  • For JWT strategy: JWKS endpoint exposed at /api/auth/jwks

Endpoint schemas and statuses

MethodPathRequestSuccessErrors
GET/api/auth/sessioncookie or Authorization200 { user, session } or { user: null, session: null }401 { error: 'unauthorized' }
POST/api/auth/signin/:providerquery callbackUrl?302 redirect to provider400 { error: 'invalid_provider' }
GET/api/auth/callback/:providerprovider params302 redirect to callbackUrl and sets cookies400 { error: 'oauth_error', code }
POST/api/auth/logoutnone200 { ok: true } and clears cookies-
GET/api/auth/jwksnone200 { keys: Jwk[] }-

Runnable examples

Sign in link (UI)
export function SignIn() {
  return (
    <a href="/api/auth/signin/github?callbackUrl=/dashboard">
      Continue with GitHub
    </a>
  );
}
Read session (server route)
export async function GET() {
  const res = await fetch(
    `${process.env.NEXT_PUBLIC_APP_URL}/api/auth/session`,
    {
      cache: "no-store",
      headers: { cookie: (await import("next/headers")).cookies().toString() },
    }
  );
  if (res.status === 401) return new Response(null, { status: 401 });
  const data = await res.json();
  return Response.json(data);
}
Logout
export async function POST() {
  const res = await fetch(`/api/auth/logout`, { method: "POST" });
  return Response.json({ ok: res.ok });
}
JWKS consumer (server)
const jwks = await fetch(`${process.env.NEXT_PUBLIC_APP_URL}/api/auth/jwks`, {
  next: { revalidate: 300 },
}).then((r) => r.json());

Error handling patterns

  • Always return structured JSON for API errors, e.g. { error, code, message? }
  • Map OAuth provider errors to 400 with code and minimal message
  • For unauthorized, return 401 with { error: 'unauthorized' }
Route handler pattern
export async function POST(req: Request) {
  try {
    // do work
    return Response.json({ ok: true });
  } catch (err: any) {
    const code = err?.code ?? "unknown_error";
    const status =
      code === "invalid_request" ? 400 : code === "unauthorized" ? 401 : 500;
    return Response.json(
      {
        error: code,
        message:
          process.env.NODE_ENV === "development" ? err?.message : undefined,
      },
      { status }
    );
  }
}

Performance and rate limiting

  • Cache GET /api/auth/jwks responses for 5–10 minutes
  • Keep session endpoint lightweight; avoid extra DB queries when using JWT
  • Apply rate limiting to sign-in start and callback endpoints
  • Avoid synchronous logging in request path; defer or batch logs

Security notes

  • Validate inputs (provider name, callback URLs) and reject unknown providers
  • Set appropriate CORS if exposing cross-origin clients; default to same-origin
  • Ensure cookies are secure, httpOnly, sameSite: 'lax' unless cross-site required

Troubleshooting

  • 302 loops after callback: verify baseUrl, cookie domain/path, and callback URL configuration
  • 401 from /session: ensure cookies are forwarded in server fetches and domain/path match
  • kid not found when verifying JWTs: publish rotated keys and keep overlap window active

See also

Next steps

  • Add provider configuration and complete a full sign-in flow
  • Instrument logging and add rate limiting on auth endpoints

How is this guide?