Files
shankar0123 56e2ea1ad7 docs: v2.1.0 release polish — strip internal bundle/phase tags, update status for OIDC ship
README:
- Rewrite Status block: drop the stale 'federated identity not yet
  shipped' line; flag v2.1.0 OIDC + sessions + back-channel logout
  + break-glass as early-access; encourage GitHub issues for IdP
  rough edges. (A1 framing — keep early-access umbrella, no
  SAML/WebAuthn/JIT roadmap teaser.)
- Add OIDC SSO bullet to 'What it does' covering per-IdP runbooks,
  group-claim → role mapping, AES-256-GCM client_secret encryption,
  JWKS auto-refresh, PKCE-S256, RFC 9700 §4.7.1 pre-login binding,
  RFC 9207 iss check, __Host- cookies, CSRF rotation, idle+absolute
  expiry, BCL, break-glass admin.
- Update Security paragraph: three auth paths (API keys / OIDC /
  break-glass), HMAC-signed sessions, CSRF rotation, RFC OIDC BCL.
- Correct CI coverage thresholds against
  .github/coverage-thresholds.yml (service 70%, handler 75%,
  crypto 88%, auth packages 85-95%); 'static analysis' replaces
  the inflated '11 linters' claim (actual count is 4 active).

Docs B3 sweep — strip operator-facing 'Bundle N' / 'Phase N' tags:
- docs/operator/auth-threat-model.md — rewrite intro; rename 5 H2
  sections (API-key + RBAC defenses / OIDC + sessions + break-glass
  defenses / OIDC + sessions threat catalogue / Closed federated-
  identity threats / Future-work threats); clean ~12 H3/prose hits.
- docs/operator/rbac.md — strip Bundle 1 framing from intro,
  scope_id deferral note, MCP tools section, day-0 bootstrap, and
  'Where to look next'.
- docs/operator/auth-benchmarks.md — drop 'Phase 14' framing from
  title intro, hardware floor caption, result table caption,
  methodology, and pre-merge audit section.
- docs/operator/security.md — already cleaned earlier this session
  (RBAC / day-0 / approval-bypass / OIDC federation / sessions /
  OIDC first-admin / break-glass H3s).
- docs/operator/oidc-runbooks/{index,keycloak,authentik,okta,
  azure-ad}.md — strip Auth Bundle 2 framing + Phase 10/3/4
  references; replace with feature-name prose.
- docs/operator/legacy-clients-tls-1.2.md — drop Bundle F / M-023
  audit-reference framing; keep CWE-326.
- docs/operator/database-tls.md — drop Bundle B / M-018 framing
  from intro + Helm section.
- docs/operator/runbooks/disaster-recovery.md — drop 'Production
  hardening II Phase 10' status callout.
- docs/migration/oidc-enable.md — retitle 'Enable OIDC SSO';
  strip Bundle 1/2 framing from prereqs, troubleshooting, related
  docs; update __Host- cookie callout from 'audit MED-14' to
  v2.1.0-BREAKING.
- docs/migration/api-keys-to-rbac.md — strip Bundle 1 framing from
  intro, migration table, IsAdmin section, and cross-references.
- docs/migration/acme-from-cert-manager.md — strip residual
  'Phase 5' tags from cert-manager integration test references.
- docs/reference/configuration.md — retitle Auth section.
- docs/reference/profiles.md — strip Bundle 1 Phase 9 framing
  from RequiresApproval section + Related list.
- docs/reference/auth-standards-implemented.md — rewrite intro
  (API-key + RBAC + OIDC + sessions + back-channel logout +
  break-glass); rename 'Bundle 1 (RBAC) standards covered
  separately' H2; clean per-row Phase references.
- docs/README.md — rewrite nav-table entries to drop Bundle 1/2
  parentheticals; retitle 'Enable OIDC SSO' migration entry.

No code or test changes; pure operator-facing prose polish for
the v2.1.0 tag.
2026-05-11 16:54:07 +00:00

7.2 KiB

Authentik OIDC runbook

Last reviewed: 2026-05-10

This runbook wires certctl's OIDC SSO surface against Authentik, a free / open-source IdP that runs on-prem or self-hosted. Authentik shares the canonical "string-array groups claim under the groups key" pattern with Keycloak — the differences are in the admin console UX and the explicit "property mapping" abstraction.

For the canonical reference + mental model, read keycloak.md first; this runbook only documents the Authentik-specific deltas.

Prerequisites

On the Authentik side:

  • Authentik ≥ 2024.10 (stable channel).
  • Admin access to the Authentik admin console at https://<authentik-host>/if/admin/.
  • Network reachability from certctl-server to https://<authentik-host>/application/o/<application-slug>/.well-known/openid-configuration.

On the certctl side: same as Keycloak — CERTCTL_CONFIG_ENCRYPTION_KEY set, an admin actor holding auth.oidc.create + auth.oidc.edit, server build ≥ v2.1.0.

IdP-side configuration

1. Create the OAuth2 / OpenID Provider

In the Authentik admin console:

Applications → Providers → Create:

  • Type: OAuth2/OpenID Provider.
  • Name: certctl.
  • Authorization flow: default-provider-authorization-explicit-consent (or default-provider-authorization-implicit-consent if you don't want a consent screen on every login).
  • Click Next.

Protocol settings:

  • Client type: Confidential.
  • Client ID: leave the auto-generated value OR set to certctl for clarity.
  • Client Secret: copy the auto-generated value to a secure scratchpad — you'll paste it into certctl.
  • Redirect URIs/Origins: https://<your-certctl-host>:8443/auth/oidc/callback (one entry, exact match).
  • Signing Key: pick an RSA-2048 or larger key. Authentik defaults to ECDSA-P256 in newer versions; either is fine — both are in certctl's allow-list.
  • Subject mode: Based on the User's hashed ID (default; emits a stable opaque sub).
  • Include claims in id_token: on.
  • Click Finish.

2. Create the Application

Applications are how Authentik attaches a Provider to users + groups + policies.

Applications → Applications → Create:

  • Name: certctl.
  • Slug: certctl (becomes part of the issuer URL: https://<authentik-host>/application/o/certctl/).
  • Provider: pick the certctl provider you just created.
  • Policy engine mode: any (default).
  • Click Create.

3. Configure the groups property mapping

Authentik emits group claims via "property mappings" — explicit objects rather than Keycloak's mapper-on-the-client model.

By default, the Authentik default-OAuth Mapping: Proxy outpost scope already includes the user's groups under a groups claim (string-array, matches what certctl expects). To verify or override:

Customization → Property Mappings → Filter "Scope Mapping":

  • Find or create one named groups with scope groups and expression:
    return [group.name for group in user.ak_groups.all()]
    
  • Description: Emits the user's group names as a string-array claim.

Then on the Provider → certctl → Edit → Advanced protocol settings, ensure Scopes includes groups (and profile and email if you want richer User records on the certctl side).

4. Create the groups + assign users

Directory → Groups → Create:

  • Name: certctl-engineers. Repeat for certctl-viewers (and optionally certctl-admins).

Directory → Users → → Edit → Groups: pick the appropriate certctl-* group(s) for each user.

5. (Optional) Bind the application to specific groups

If you want certctl to reject login attempts from users outside the certctl-* groups at the IdP layer (defense-in-depth on top of certctl's fail-closed ErrGroupsUnmapped):

Applications → certctl → Policy / Group / User Bindings → Create binding:

  • Type: Group.
  • Group: pick the union of certctl-* groups you want to allow.
  • Enabled: on.

certctl-side configuration

Identical to Keycloak — only the issuer URL differs:

curl -X POST https://<your-certctl-host>:8443/api/v1/auth/oidc/providers \
  -H "Authorization: Bearer ${CERTCTL_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Authentik",
    "issuer_url": "https://authentik.example.com/application/o/certctl/",
    "client_id": "<paste-the-client-id>",
    "client_secret": "<paste-the-client-secret>",
    "redirect_uri": "https://certctl.example.com:8443/auth/oidc/callback",
    "groups_claim_path": "groups",
    "groups_claim_format": "string-array",
    "fetch_userinfo": false,
    "scopes": ["openid", "profile", "email", "groups"],
    "iat_window_seconds": 300,
    "jwks_cache_ttl_seconds": 3600
  }'

Authentik emits groups in the ID token by default once the property mapping is configured. The scopes array MUST include groups to trigger the claim emission — Authentik is stricter than Keycloak about scope-gating claims.

Add the group→role mappings the same way as Keycloak: certctl-engineersr-operator, certctl-viewersr-viewer.

Verification

End-to-end login + audit + Sessions checks are identical to Keycloak.

Authentik-specific check: the audit row's details.subject will be Authentik's hashed user ID (a 64-char hex), not the username. This is intentional and correct — the sub claim must be opaque + stable across user-attribute changes.

JWKS-rotation drill: Authentik rotates signing keys via System → Tokens & App Passwords → Certificates (rename of "Crypto" in newer versions). Add a new RSA-2048 cert, switch the Provider's Signing Key to the new one, then click "Refresh discovery cache" in certctl's GUI to evict the cache.

Troubleshooting

Provider creation fails with "could not load discovery document". The issuer URL needs the trailing slash for some Authentik versions: https://authentik.example.com/application/o/certctl/ (slash after the slug). Without the slash, Authentik returns a 301 redirect that Go's HTTP client follows but discovery parsing chokes on the redirect target.

Login completes but user lands on "no roles assigned". Decode the ID token at jwt.io against Authentik's JWKS. Check whether the groups claim is present + non-empty. If empty, the property mapping isn't wired — go back to step 3.

groups claim missing entirely. Authentik gates the groups claim behind the groups scope. Verify:

  • The certctl OIDCProvider config has "scopes": ["openid", "profile", "email", "groups"].
  • The Authentik provider's "Scopes" list includes groups.

Authentik emits the user's full DN as the sub claim. Some Authentik configurations use Subject mode: Based on the User's email which surfaces the email as sub. This works but tightly couples certctl's User table to email mutability; recommend switching to "hashed ID" mode for new deployments. Existing User rows in certctl's users table will have email-shaped oidc_subject columns; that's fine and stable as long as the user's email never changes.

Validation checklist

Same as keycloak.md, with Authentik-specific values for issuer URL + group names + signing-key rotation steps.

Sign-off: _______________ (operator) on _______________ (date).