Skip to content

Authentication

NimbleBrain uses pluggable authentication adapters configured via instance.json in the work directory. The adapter determines how users identify themselves — from simple API keys to full enterprise SSO.

When no instance.json exists, NimbleBrain runs without authentication. All requests receive a default developer identity and a default workspace is created automatically. This is intended for local development only.

Configure authentication by setting the adapter field in instance.json:

AdapterBest forIdentity providers
localSelf-hosted, small teamsAPI keys + session cookies
oidcBYO identity providerAny OIDC-compliant provider (Auth0, Keycloak, Okta, Google, Entra, etc.)
workosManaged enterprise SSO + directory syncGoogle Workspace, Microsoft Entra, Okta, OneLogin, and more via WorkOS

Per-user API keys stored locally with bcrypt hashing. Users authenticate via Bearer token or session cookie.

instance.json
{
"auth": {
"adapter": "local"
}
}

Generic OIDC adapter that works with any provider supporting OpenID Connect Discovery. NimbleBrain verifies JWTs against the provider’s JWKS endpoint and restricts access to allowed email domains.

instance.json
{
"auth": {
"adapter": "oidc",
"issuer": "https://your-provider.example.com",
"clientId": "your-client-id",
"allowedDomains": ["yourcompany.com"]
}
}
FieldRequiredDescription
issuerYesOIDC issuer URL. Must serve a /.well-known/openid-configuration endpoint.
clientIdYesOAuth client ID from your provider.
allowedDomainsYesEmail domains permitted to authenticate (e.g., ["yourcompany.com"]).
jwksUriNoJWKS URI override. If omitted, discovered automatically via the issuer’s .well-known metadata.

Use this adapter when you already have an OIDC-compliant identity provider and want direct integration without a middleman.

Managed enterprise authentication via WorkOS. This adapter adds capabilities beyond raw OIDC:

  • Enterprise SSO — Google Workspace (SAML), Microsoft Entra, Okta, OneLogin, PingFederate, and any generic SAML/OIDC provider
  • Directory Sync — Automatic user provisioning and deprovisioning from your identity provider
  • Admin Portal — Self-serve SSO and directory configuration for your end users
  • Organization scoping — Restrict access to members of a specific WorkOS organization
instance.json
{
"auth": {
"adapter": "workos",
"clientId": "client_...",
"redirectUri": "https://app.example.com/v1/auth/callback"
}
}
FieldRequiredDescription
clientIdYesWorkOS Client ID (client_...).
redirectUriYesOAuth callback URL (must match your WorkOS environment settings).
organizationIdNoWorkOS Organization ID — scopes authentication to a specific organization.
apiKeyNoWorkOS API key. Can also be set via the WORKOS_API_KEY environment variable.

On first login, NimbleBrain verifies the user’s organization membership, syncs their profile, and provisions a private workspace automatically.

These endpoints are active when using the oidc or workos adapter:

  • GET /v1/auth/provider — returns the auth adapter type and configuration
  • GET /v1/auth/authorize — redirects to the provider’s authorization page
  • GET /v1/auth/callback — handles the OAuth callback and sets a session cookie
  • POST /v1/auth/refresh — refreshes the session token

Include your API key in the Authorization header:

Terminal window
curl http://localhost:27247/v1/bootstrap \
-H "Authorization: Bearer your-api-key"

The server performs a constant-time comparison to prevent timing attacks. If the key does not match, the server returns 401 with an empty body.

For browser-based clients, exchange the API key for an HttpOnly cookie using the login endpoint. The cookie is automatically sent on subsequent requests.

Validate the API key and receive a session cookie.

Request body:

FieldTypeRequiredDescription
keystringYesThe API key to validate

Response:

{
"ok": true
}

On success, the response includes a Set-Cookie header:

Set-Cookie: nb_session=<key>; HttpOnly; SameSite=Strict; Path=/; Max-Age=604800

Cookie properties:

  • HttpOnly — not accessible from JavaScript
  • SameSite=Strict — only sent on same-site requests
  • Secure — set when the server is not running on localhost
  • Max-Age=604800 — 7-day expiry

Error responses:

StatusCondition
400Missing or non-string key field
401Key does not match NB_API_KEY

Example:

Terminal window
curl -X POST http://localhost:27247/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"key": "your-secret-key"}' \
-c cookies.txt

Clear the session cookie. Requires authentication.

Response:

{
"ok": true
}

The response sets the cookie’s Max-Age to 0, which instructs the browser to delete it.

Example:

Terminal window
curl -X POST http://localhost:27247/v1/auth/logout \
-H "Authorization: Bearer your-secret-key"

Check whether the current session is valid. Requires authentication. Returns 200 if authenticated, 401 otherwise.

Response:

{
"authenticated": true
}

Example:

Terminal window
curl http://localhost:27247/v1/auth/session \
-b cookies.txt

When both a Bearer token and a session cookie are present, the Bearer token takes precedence. The server checks the Authorization header first, then falls back to the nb_session cookie.

  • Minimum length: 8 characters
  • Keys shorter than 16 characters produce a startup warning
  • The server refuses to start if the key is shorter than 8 characters

CORS behavior depends on the auth configuration and ALLOWED_ORIGINS:

Auth modeALLOWED_ORIGINSBehavior
Dev (no auth)N/AAccess-Control-Allow-Origin: *
Auth configuredSet (comma-separated)Only matching origins get CORS headers with credentials: true
Auth configuredNot setSame-origin only (no CORS headers)

Set ALLOWED_ORIGINS as a comma-separated list of origins:

Terminal window
export ALLOWED_ORIGINS="https://app.example.com,https://staging.example.com"

The server always responds to OPTIONS preflight requests with:

Access-Control-Allow-Methods: GET, POST, PATCH, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization, Mcp-Session-Id, Last-Event-ID, Mcp-Protocol-Version, X-Workspace-Id
Access-Control-Expose-Headers: Mcp-Session-Id, Mcp-Protocol-Version

Every failed authentication attempt is logged to stderr with the client IP and timestamp:

[nimblebrain] AUTH FAIL ip=203.0.113.42 timestamp=2025-10-15T14:30:00.000Z