Hadrian is experimental alpha software. Do not use in production.
Hadrian

Authentication

Authentication modes, API keys, SSO, JWT, and reverse proxy authentication

Hadrian uses a single [auth.mode] setting that determines how all requests are authenticated across both the API and the admin panel.

Auth Modes

Configure authentication by setting [auth.mode] in hadrian.toml. Four modes are available:

ModeConfigDescription
nonetype = "none"No authentication. Development only.
api_keytype = "api_key"API key required for all access (admin panel + API).
idptype = "idp"Per-org SSO with session cookies, JWT validation, and API keys.
iaptype = "iap"Reverse proxy headers for admin access, API keys for API access.

Supporting Config Sections

SectionUsed byPurpose
[auth.api_key]api_key, idp, iapAPI key settings (header name, key prefix, cache TTL)
[auth.session]idpSession cookie settings (cookie name, secure flag, secret)
IAP fields in [auth.mode]iapIdentity and email header names from the reverse proxy

Deployment Scenarios

Pick the scenario closest to your deployment. Each includes a minimal working config.

#ScenarioModeWeb UI experienceSDK / API access
1Local developmentnoneNo login, everything openNo credentials needed
2API keyapi_keyEnter API key to log inAPI key
3SSO / IdPidpPer-org SSO login, session cookie handles everythingAPI key or per-org JWT
4Reverse proxy (IAP)iapProxy handles loginAPI key

1. Local development

No authentication. All requests proceed anonymously. The admin panel, chat UI, and API are all open.

[auth.mode]
type = "none"

Never use type = "none" in production. Requests are unauthenticated and usage cannot be attributed to users.

2. API key

API keys required for all access. The admin panel shows an "enter API key" login screen. SDK and API clients send keys via headers.

[auth.mode]
type = "api_key"

[auth.api_key]
header_name = "X-API-Key"
key_prefix = "gw_"
cache_ttl_secs = 300

Getting started: Use the bootstrap API key to create the initial organization and users. Then create API keys via the admin panel for each user. Paste a key into the chat UI settings or pass it to the OpenAI SDK.

3. SSO / IdP

Per-org SSO for browser sessions, with API key and JWT support on /v1/* endpoints. Each organization configures their own OIDC or SAML provider through the Admin UI at runtime.

[auth.mode]
type = "idp"

[auth.api_key]
key_prefix = "gw_"
cache_ttl_secs = 300

[auth.session]
cookie_name = "hadrian_session"
secure = true
secret = "${SESSION_SECRET}"

[auth.bootstrap]
api_key = "${HADRIAN_BOOTSTRAP_KEY}"
auto_verify_domains = ["acme.com"]

Getting started: Use the bootstrap API key to create organizations. Each org admin then configures their IdP via the SSO Admin Guide. Users are auto-provisioned on first SSO login. Per-org SSO configs automatically enable JWT validation on /v1/* endpoints -- no global OIDC configuration needed.

There is no global OIDC or SAML config in hadrian.toml. All identity provider configurations are per-organization and managed through the Admin UI. This enables multi-tenant deployments where each organization uses a different IdP.

4. Reverse proxy (IAP)

Trust identity headers from an authenticating reverse proxy (Cloudflare Access, Tailscale, oauth2-proxy, etc.) for admin access. API access uses API keys.

[server.trusted_proxies]
cidrs = ["173.245.48.0/20", "103.21.244.0/22"]

[auth.mode]
type = "iap"
identity_header = "Cf-Access-Authenticated-User-Email"
email_header = "Cf-Access-Authenticated-User-Email"

[auth.api_key]
key_prefix = "gw_"
cache_ttl_secs = 300

Getting started: Configure your reverse proxy to authenticate all traffic to Hadrian and forward identity headers. Create API keys via the admin panel for chat UI and SDK access.

Always configure [server.trusted_proxies] to prevent header spoofing. Without this, attackers can forge identity headers and impersonate any user.

In iap mode, both proxy headers and API keys work for authentication. The proxy authenticates admin panel access, while API keys authenticate /v1/* requests.

Architecture

Default Behavior

When [auth.mode] is omitted from hadrian.toml, authentication defaults to type = "none". This means:

  • API requests proceed without credentials. A default anonymous user and organization are created for usage tracking.
  • Admin routes are unprotected -- the web UI works without login.
  • RBAC is permissive -- all authorization checks pass automatically.

Credential Handling in No-Auth Mode

Even with type = "none", the gateway validates credentials that are explicitly provided:

ScenarioResult
No credentials sentRequest proceeds as anonymous
Valid credentials sentRequest proceeds as the authenticated user
Invalid credentials sent (expired JWT, wrong API key, etc.)401 Rejected

This means you can incrementally adopt authentication -- existing anonymous clients continue working while authenticated clients get identity tracking and RBAC. However, clients must not send placeholder or invalid credentials.

API Key Authentication

API keys are the primary method for programmatic access to /v1/* endpoints. Keys are created via the Admin UI or API and validated against the database.

# Using X-API-Key header
curl -H "X-API-Key: gw_live_abc123..." \
  https://gateway.example.com/v1/chat/completions

# Using Authorization header (OpenAI-compatible)
curl -H "Authorization: Bearer gw_live_abc123..." \
  https://gateway.example.com/v1/chat/completions

Key features:

  • Configurable prefix (gw_live_, gw_test_, etc.)
  • Budget limits (daily/monthly caps)
  • Expiration dates
  • Usage tracking per key
  • Owner binding (org, team, project, or user)

In api_key mode, API keys authenticate everything -- including the admin panel. In idp and iap modes, API keys authenticate /v1/* endpoints alongside the mode's primary mechanism.

Format-Based Detection

When both API keys and JWTs are accepted (in idp mode), the gateway uses format-based detection:

  • X-API-Key header: Always validated as an API key
  • Authorization: Bearer header: Tokens starting with the API key prefix (default gw_) are validated as API keys; all others are validated as JWTs

Per-Org JWT Routing

In idp mode, per-org SSO configurations automatically enable JWT validation on /v1/* endpoints. There is no global JWT configuration in the config file -- all JWT validation derives from per-org SSO configs.

How it works:

  1. Decode iss claim -- Extract the issuer from the JWT without verifying the signature yet
  2. Per-org registry lookup -- Search the GatewayJwtRegistry for validators matching that issuer
  3. Lazy-load from DB -- On cache miss, query the database for enabled OIDC SSO configs with that issuer, perform OIDC discovery to fetch the JWKS URI, and build a validator
  4. Validate -- Verify the signature, expiry, audience, and issuer against the matched validator

Key behaviors:

  • Validators are cached across requests -- JWKS keys are not re-fetched per request
  • Unknown issuers are negatively cached for 60 seconds to prevent DB query amplification
  • When an SSO config is created or updated via the Admin API, the registry is updated immediately
  • Multiple organizations can share the same issuer (each gets its own validator with its own audience)

Per-org JWT routing only applies in idp mode. Per-org SSO configs provide all JWT validation automatically -- no global JWT configuration is needed in the config file.

Session-Based Authentication

In idp mode, browser sessions use session cookies set during the SSO login flow. The session cookie authenticates both /admin/* and /v1/* requests, so users log in once and the chat UI works immediately -- no API key needed.

OIDC / SSO

Full OpenID Connect flow with browser redirects. Works with any OIDC-compliant identity provider:

ProviderNotes
OktaEnterprise SSO with MFA
Auth0Developer-friendly with many integrations
KeycloakSelf-hosted, open source
Azure AD / Entra IDMicrosoft 365 integration
Google WorkspaceGoogle account authentication
OneLoginEnterprise identity management

SAML 2.0

SAML 2.0 authentication for enterprise identity providers that require the Security Assertion Markup Language protocol:

ProviderNotes
AD FSWindows Server Active Directory
Azure AD / Entra IDEnterprise SAML application
OktaNative SAML application templates
KeycloakSelf-hosted, open source
PingFederateEnterprise identity federation

Both OIDC and SAML providers are configured per-organization through the Admin UI, not the config file. See the SSO Admin Guide and SAML Admin Guide for setup instructions.

Reverse Proxy Authentication

In iap mode, Hadrian trusts identity headers from an authenticating reverse proxy:

ServiceHeaders
Cloudflare AccessCf-Access-Authenticated-User-Email
oauth2-proxyX-Forwarded-User, X-Forwarded-Email
TailscaleTailscale-User-Login
Authelia / AuthentikRemote-User, Remote-Groups
PomeriumX-Pomerium-Claim-Email

Always configure trusted proxies to prevent header spoofing. Without this, attackers can forge identity headers.

Just-in-Time Provisioning

Automatically create users and add them to organizations when they first authenticate via SSO.

How it works:

  1. User authenticates via OIDC or SAML
  2. Gateway checks if user exists in database
  3. If not, creates user with attributes from the identity token
  4. Adds user to the configured organization with default role
  5. Optionally adds user to a default team

Capabilities:

  • Create users on first login
  • Assign to organization and team
  • Map IdP groups to teams (SSO group mappings)
  • Sync attributes on subsequent logins
  • Restrict by email domain

JIT provisioning creates users on first login. For real-time provisioning and deprovisioning (required for SOC 2/HIPAA compliance), see SCIM Provisioning.

SSO Group Mappings

Map identity provider groups to Hadrian teams for automatic team assignment during provisioning.

IdP GroupTeamRolePriority
Engineersplatformmember0
SeniorEngineersplatformlead10
PlatformAdminsplatformadmin20

When a user belongs to multiple groups mapping to the same team, the highest priority determines the role.

Authorization (RBAC)

After authentication, authorization determines what users can access. Hadrian uses CEL (Common Expression Language) for fine-grained access control.

Example policies:

# Super admins have full access
[[auth.rbac.policies]]
name = "super-admin"
condition = "'super_admin' in subject.roles"
effect = "allow"
priority = 100

# Users can only access resources in their organization
[[auth.rbac.policies]]
name = "org-isolation"
condition = "context.org_id in subject.org_ids"
effect = "allow"
priority = 10

API authorization extends CEL policies to /v1/* endpoints for:

  • Model access control (restrict GPT-4 to premium users)
  • Token limits by tier
  • Feature gating (tools, file search, reasoning)
  • Time-based access (business hours only)

On this page