Platform

Auth & RBAC

Sign-in is provider-based (no local passwords by default), roles gate what you can do, and a visibility model gates what you can see. The API trusts identity only from the frontend proxy, sealed with a shared secret.

Sign-in providers

Authentication is NextAuth (Auth.js v5), entirely inside the Next.js app — no external gateway. Each provider activates automatically when its credentials are present in the environment:

ProviderEnv vars
GitHubGITHUB_ID · GITHUB_SECRET
GoogleGOOGLE_ID · GOOGLE_SECRET
Microsoft Entra IDAUTH_MICROSOFT_ENTRA_ID_ID · _SECRET · _ISSUER

Also required: AUTH_SECRET (session signing). The OAuth callback URL is {origin}/api/auth/callback/{provider} — register it on each provider app.

Each provider’s app must list your exact origins as redirect URIs (e.g. https://kaveon.vercel.app/…and http://localhost:3000/…). A missing origin is the usual cause of a “redirect_uri not associated” error.

Roles

Four roles, ordered by privilege, gate what you can do in the API. Through sign-in a user resolves to Admin (email in AUTH_ADMIN_EMAILS) or Viewer:

RoleCan
ViewerRead published dashboards and charts
Analyst+ Run SQL, build charts and datasets
Editor+ Publish content, delete
Admin+ Manage users, data sources, metadata server, AI keys

Admins are seeded from AUTH_ADMIN_EMAILS (comma-separated). Every state-changing endpoint is guarded by a require_min_role("…") dependency — a lower role gets 403 forbidden.

The four-role ladder lives in the API’s authorization layer (Viewer / Analyst / Editor / Admin). The NextAuth sign-in itself resolves each user to just two of them: Admin when their email is listed in AUTH_ADMIN_EMAILS, otherwise Viewer.

Visibility

Independent of role, each dataset, chart, and dashboard has a visibility level — private (owner), internal (signed-in users), published — enforced by a can_read()helper on every list/get call.

The proxy trust contract

The browser never holds an API token. The Next.js proxy is the only ingress and stamps identity headers server-side:

text
kaveon-web  /api/kaveon/[...path]
   ├─ reads the NextAuth session (server-side)
   ├─ injects  X-User-Email · X-User-Name · X-User-Role
   └─ signs the request with  X-Proxy-Secret = KAVEON_PROXY_SECRET
        ▼
kaveon-api  trusts X-User-* ONLY when the secret matches

The same KAVEON_PROXY_SECRET must be set on both the web and API deployments.

Hardening built in

  • Identifier injection — all user identifiers are quoted/escaped before hitting SQL.
  • Operator injection — filter operators are validated against a strict allowlist.
  • Credential exposure — the connection_string column is stripped from every API response.
  • Identity spoofing — raw x-user-email headers are rejected unless the proxy secret matches.
  • Information disclosure — internal errors are logged server-side; clients get a generic message.