Skip to content
v0.3.8GitHub

Authentication: OIDC and local login

Verified against cerbix f5240f5Report a problem ↗

cerbix authenticates people with any OpenID Connect provider, with built-in local passwords, or with both. Authorization always happens inside cerbix, through its own roles. This page covers both sign-in methods, the role model and the credentials that pipelines use.

Method Enable with Best for
OIDC oidc.issuer, oidc.client_id, oidc.redirect_url Teams with an identity provider (Keycloak, Auth0, Okta, Google, Entra ID)
Local login local.enabled: true Bootstrap, isolated installs, a break-glass account next to SSO

Both methods need database.dsn: users and sessions live in PostgreSQL. Both issue the same server-side session: an opaque HttpOnly, SameSite=Lax cookie (cerbix_session by default) whose hash is stored in the database. session.ttl defaults to 24h; a saved login policy can override it. Set session.secure: false only for plain-HTTP development.

The login page asks GET /auth/config which methods are active and renders the OIDC button with its label.

OIDC in cerbix is authentication only. The flow is Authorization Code with PKCE (S256) and a nonce. cerbix verifies the ID token against the issuer’s discovery document, then reads the email, email_verified, name and preferred_username claims. Group or role claims are not read; access comes from cerbix memberships.

The first login provisions the user, keyed on the issuer and the sub claim (stored as oidc_sub). A changed email or display name updates the same user. A new user has no memberships and sees nothing until an organization admin adds them.

Key Default Meaning
oidc.issuer empty (OIDC off) Issuer URL used for discovery
oidc.client_id, oidc.client_secret — Client credentials registered at the provider
oidc.redirect_url — https://<host>/auth/callback
oidc.scopes ["openid", "email", "profile"] Requested scopes
oidc.button_label Continue with SSO Text of the login button
oidc.post_logout_redirect_url empty Where /auth/logout sends the browser
oidc.bootstrap_admin_emails [] Emails promoted to global admin on login, only when the provider marks the email verified

The YAML is a bootstrap seed. A global admin can change the provider at runtime under Settings → Administration → Authentication (PUT /api/v1/settings/oidc). After the first save, the database settings override the YAML.

Discovery never blocks startup. If the provider is unreachable, cerbix starts, logs oidc_build_failed, and retries every 30 s. Until it succeeds, /auth/login answers 503 and local login keeps working.

In the realm you use for cerbix, create a client:

  • Client type: OpenID Connect, Client ID: cerbix.
  • Client authentication: on (a confidential client with a secret).
  • Standard flow: on. Implicit flow: off. Service accounts roles: on only if pipelines will use client-credentials tokens.
  • Valid redirect URIs: https://cerbix.example.com/auth/callback.
  • Valid post logout redirect URIs: https://cerbix.example.com/*.
  • Keep email and profile as default client scopes so the ID token carries the email and name claims.

Then point cerbix at the realm:

oidc:
issuer: "https://sso.example.com/realms/cerbix"
client_id: "cerbix"
client_secret: "${CERBIX_OIDC_CLIENT_SECRET}"
redirect_url: "https://cerbix.example.com/auth/callback"
button_label: "Continue with Keycloak"
post_logout_redirect_url: "https://cerbix.example.com/"
bootstrap_admin_emails: ["admin@example.com"]

The issuer must be the URL Keycloak puts in the token’s iss claim. If cerbix reaches Keycloak through an internal hostname, configure Keycloak’s public hostname so both agree.

With local.enabled: true, users sign in with an email and password at POST /auth/local/login. Passwords are hashed with argon2id; only the hash is stored. A wrong password and an unknown email return the same 401, and both run the same argon2id check, so response time does not reveal which accounts exist.

Key Default Meaning
local.min_password_length 8 Minimum password length
local.login_rate_limit_per_minute 10 Attempts per client IP per minute; 0 disables. Above it: 429
security.admin_email, security.admin_password empty On an empty database, creates a global admin at startup

The bootstrap password is never generated or logged; without it no admin is created. Behind a reverse proxy, set server.trusted_proxy_count or server.trusted_proxy_cidrs so the rate limiter sees the real client IP.

Local accounts can enroll a TOTP authenticator (RFC 6238: 6 digits, 30 s period) under Settings → Account → Security. Enabling it shows 8 single-use recovery codes once. After a correct password, the login then needs a current code or an unused recovery code; without one it returns 401 with "totp_required": true.

A global admin can require TOTP with the login policy (Settings → Administration → Authentication): none, admins or all. The policy applies to local sign-ins only. SSO users get multi-factor from their identity provider. The same policy can restrict sign-in to listed email domains; for OIDC this requires a verified email claim.

Self-service reset works when local login is on and SMTP is configured (mail.smtp_host, mail.from, mail.public_base_url). POST /auth/local/reset/request always answers 200, so it reveals nothing about accounts. The emailed link is valid for one hour and works once.

Access is a membership: a user, a role and a scope (an organization or one project). An organization membership applies to every project in it.

Role Scope Grants
Global admin Instance Everything, plus organizations, users, instance settings, agent tokens and file-provider diagnostics
org_admin Organization Manage the organization: projects, members, API tokens, webhooks; full project access
project_admin Project Editor rights plus release-gate overrides
editor Organization or project Create and change monitors, services, channels, gate policies; record changes
viewer Organization or project Read access and gate evaluation

A resource outside your memberships answers 404, so its existence stays hidden. A visible resource you may not change answers 403.

Organization admins manage members under Settings → Organization → Members. A user must sign in once before they can be added.

An organization admin creates a token under Settings → Organization → API tokens or with POST /api/v1/organizations/{orgID}/tokens (name, role, optional project_id, optional actions). The secret starts with cbx_ and is shown once. Send it as Authorization: Bearer <token>.

actions narrows the role to an allow-list. A deploy pipeline needs only this:

{ "name": "ci-checkout", "role": "editor", "project_id": "<project-id>", "actions": ["gate:evaluate", "change:record"] }

The list is fixed at creation; create a new token to change it. Revoke a token with DELETE /api/v1/tokens/{tokenID}.

While OIDC is active, cerbix also accepts a bearer JWT issued by the same provider, for example a Keycloak service account. The signature and issuer are verified; the audience is not checked. The service account becomes a user keyed on its sub and gets exactly the memberships you grant it.

Agent tokens for HTTP-pull probers are a separate credential; see Geo-distributed probers.