Skip to main content

Authentication

Feature row 10 — Sprint LIC Phases 3–4

Password and Clerk are available on every plan. Generic OIDC and SAML 2.0 require the Enterprise plan.

Provider options

Behavry ships with four authentication providers that sit behind a common AuthProvider abstraction. You pick one per tenant and the rest of the product is provider-agnostic.

ProviderWhen to use
ClerkSaaS default. Zero-setup. Google / Microsoft / email SSO included.
OIDC (generic)Enterprise IdP integration: Entra ID, Okta, Ping, Auth0, Keycloak, anything OIDC-compliant
SAML 2.0Enterprise IdPs that only speak SAML (ADFS, older Okta configurations)
PasswordLocal dev, air-gapped deployments, emergency break-glass

Provider selection lives in TenantConfig.auth_provider; setup is in Settings → Auth. Changing providers requires admin + super-admin approval for safety.

Clerk (default)

Behavry's SaaS and most Hybrid deployments use Clerk by default. Clerk handles sign-up, sign-in, password reset, MFA, social connections, and session management. Behavry consumes Clerk JWTs via JWKS and maps Clerk orgs to Behavry tenants.

What Behavry needs

  • Clerk publishable keyVITE_CLERK_PUBLISHABLE_KEY on the dashboard
  • Clerk secret keyBEHAVRY_CLERK_SECRET_KEY on the backend (for org provisioning)
  • JWT template — Behavry ships a recommended Clerk JWT template that includes o.id, o.slug, and o.rol claims
note

The commercial dashboard, demo.behavry.ai, and docs.behavry.ai each run against separate Clerk instances. This page covers the commercial dashboard's Clerk configuration only. The docs site's sign-in flow is described under Accounts & Sign-In and is intentionally not tenant-aware.

How tenant mapping works

Each Clerk Organization (o.id) maps 1:1 to a Behavry tenant. When a user signs in for the first time with a new org, the backend auto-provisions a matching tenant (backend/behavry/admin/clerk_provider.py). The org role (o.rol) maps to the Behavry role: org:adminadmin, org:policy_authorpolicy_author, etc.

Token sync

The dashboard runs a ClerkTokenSync component that refreshes the Clerk JWT every 50 seconds (Clerk tokens expire at 60) and writes it to localStorage['behavry_admin_token']. The API client reads from the same key, so the rest of the frontend is Clerk-unaware.

Generic OIDC

For tenants that have an existing corporate IdP.

Configuration

Settings → Auth → OIDC. You provide:

  • Issuer URL — e.g. https://login.microsoftonline.com/{tenant}/v2.0
  • Client ID and client secret (client credentials or authorization code flow)
  • Scopes — default openid profile email, add group scopes if your IdP exposes them
  • Redirect URI — Behavry exposes /api/v1/auth/sso/oidc/callback
  • Attribute mapping — how IdP claims map to Behavry user fields:
    • email (required)
    • name
    • groups → Behavry role (rule-based: if groups contains behavry_admin → role admin, else viewer)

Flow

  1. User hits the dashboard, clicks "Sign in with SSO"
  2. Dashboard redirects to /api/v1/auth/sso/oidc/start
  3. Backend redirects to the IdP's authorize endpoint with PKCE
  4. User authenticates at IdP, returns with a code
  5. Backend exchanges code for tokens, validates id_token, maps claims, issues a Behavry session JWT
  6. Dashboard receives the session and the user lands on the home page

All of this lives in backend/behavry/admin/oidc_provider.py and sso_routes.py.

SAML 2.0

SAML is structurally similar to OIDC but uses XML assertions instead of JWTs.

Configuration

Settings → Auth → SAML.

  • IdP metadata URL (preferred) or uploaded metadata XML
  • Attribute mapping — same idea as OIDC: email, name, groups → role
  • ACS URL — Behavry exposes /api/v1/auth/sso/saml/acs
  • SLO URL — single logout support, optional

Flow

Standard SP-initiated SAML:

  1. User clicks "Sign in with SSO"
  2. Behavry builds an AuthnRequest, redirects to IdP
  3. IdP authenticates, posts a SAML Response to the ACS URL
  4. Behavry validates the signature against the IdP's certificate, extracts attributes, issues a session

Source: backend/behavry/admin/saml_routes.py.

Password

For local development, air-gapped deployments, and emergency access.

  • Usernames and bcrypt hashes in admin_users
  • BEHAVRY_ADMIN_USERNAME and BEHAVRY_ADMIN_PASSWORD env vars bootstrap the first admin on startup
  • POST /api/v1/admin/login returns a session JWT

Password auth is always reachable at /api/v1/admin/login even when a tenant uses Clerk / OIDC / SAML — this is intentional as a break-glass path. Disable it by deleting every password-enabled admin user.

Role mapping

Regardless of provider, incoming user identities land in the same four roles (see User Roles):

RoleHierarchy
viewer1
analyst2
policy_author3
admin4

Mapping happens at sign-in. Subsequent API calls carry the role in the session JWT.