Adds config/providers_oauth2/vault.yaml so human logins to OpenBao go
through Authentik SSO (bao CLI and the web UI). Machine auth (approle,
kubernetes, CI) and break-glass are unchanged and stay on the OpenBao side.
Extends the oauth2 provider module so a config may generate its own client
secret instead of reading a pre-seeded one: client_secret_vault.generate
creates a random_password and writes {client_id, client_secret} to the given
kv-v2 path. Providers without the flag keep the existing read-only data source
behaviour. This is what lets the provider land with no manual Vault seed.
Gates the new application with akP-vault-admin and nests it in
akR-global-admin, matching how every other app in this repo is bound.
The Authentik API token used by estate automation was created by hand in the
UI and pasted into Vault, so it was undocumented, unauditable and impossible
to rotate reproducibly. Model it as config instead.
Add a service_accounts config kind, discovered from config/service_accounts/
like the other kinds. Each entry creates a service_account user, an RBAC role
carrying its global permissions, its API tokens, and (optionally) a kv-v2
write publishing each token key.
Add sa-agent-api granting view_outpost, view_token and view_token_key, with a
non-expiring api token agent-api-token published to kv/service/authentik/agent-api-token.
Ben (akR-global-admin) does not see the LiteLLM tile on the Authentik user
dashboard, while ArgoCD/Grafana/Rancher appear normally. The live API shows
LiteLLM is configured identically to those apps: the app exists, its access
binding akP-litellm-admin -> litellm is present, and akR-global-admin nests
akP-litellm-admin (bidirectionally, same as the others). A CI-style plan against
live state reports "No changes" -- so this is not terraform-correctable drift,
and a plain re-apply fixes nothing. Yet check_access for Ben returns
passing=false for litellm and passing=true for the rest: a stale cached access
policy result inside Authentik.
Add an optional per-app launch_url to the providers_oauth2 config (default null,
which keeps Authentik's redirect-derived URL) and wire it to the application's
meta_launch_url. Set it for LiteLLM to its UI. This makes the dashboard tile
deterministic and, on apply, re-saves the application -- invalidating the stale
access-policy cache so Ben's (already-correct) access re-evaluates and the tile
appears.
Claude-Session: https://claude.ai/code/session_015ur3i7D2azsMAWTSVABApv
OAuth2 providers with no signing_key fall back to HS256, which RS256-only
RP clients (argocd confirmed, and the rest) reject with "unexpected
signature algorithm HS256; expected [RS256]", breaking OIDC login.
- Add data.authentik_certificate_key_pair.signing, resolving the estate's
RSA keypair by name (var.oauth2_signing_key_name, default the built-in
"authentik Self-signed Certificate").
- Default every provider's signing_key to that keypair via coalesce, so all
providers sign with RS256 while keeping the per-yaml signing_key override.
Claude-Session: https://claude.ai/code/session_015ur3i7D2azsMAWTSVABApv
## Why
All Authentik OIDC logins (ArgoCD, Grafana, Rancher, LiteLLM, NetBox) fail
with `invalid_request` / "The request is otherwise malformed". Authentik
2026.5 added an explicit `grant_types` allow-list to the OAuth2 provider
(model default = empty list). Our module never set it, so every provider has
`grant_types = []`, and `authorize.py` rejects the authorization_code grant
(`if self.grant_type not in self.provider.grant_types`) before any user auth.
## Change
- modules/authentik: add a `grant_types` field to the `providers_oauth2`
variable, defaulting to `["authorization_code", "refresh_token"]` (the
standard confidential web-app set), and wire it into
`authentik_provider_oauth2`.
## Plan
`0 to add, 5 to change, 0 to destroy` — each existing oauth2 provider's
`grant_types` goes `[] -> ["authorization_code", "refresh_token"]`; no other
attributes change. Baseline plan (pre-change) was clean (no netbox/state drift).
Claude-Session: https://claude.ai/code/session_015ur3i7D2azsMAWTSVABApv
Bring LiteLLM into the two-tier RBAC and map groups to LiteLLM roles.
- akP-litellm-admin / akP-litellm-user permission groups (bound to the litellm
app for access); added to akR-global-admin / akR-standard-user roles.
- Generic per-provider role_mappings: emit an app role claim computed from
effective (hierarchical) group membership. LiteLLM: emits `litellm_role`
(proxy_admin for akP-litellm-admin, internal_user for akP-litellm-user, else
internal_user_view_only); LiteLLM reads it via GENERIC_USER_ROLE_ATTRIBUTE.
Validated: plan 5 to add, 3 to change; generated role expression renders correctly.
- Permission/role group name now comes from the config filename (the map key),
dropping the redundant `name` field from each YAML and the object types.
- The hierarchical mapping emits an `ak_groups` claim (scope `ak_groups`) instead
of `groups`, so it never collides with the direct-groups the default profile
mapping already emits under `groups` (Authentik overrides same-key claims in an
unpredictable order). Apps request the `ak_groups` scope and read that claim.
Introduce a user -> role -> [permissions] model for app access and roles,
managed declaratively.
- Permission groups (akP-<app>-<access>) under config/permissions/: atomic units,
each names the application it grants access to.
- Role groups (akR-<role>) under config/roles/: what users are assigned to;
each nests permission groups via parents (akR-global-admin -> all *-admin,
akR-standard-user -> all *-user). Split into a separate authentik_group
resource so roles can reference permission ids without self-reference.
- Policy bindings gate each application to its permission groups (and, via
child->parent membership propagation, the roles that nest them).
- Hierarchical `groups` scope mapping: walks user groups up through .parents so
the OIDC claim includes inherited permission groups (works around
goauthentik/authentik#15579). Inert until a provider requests the `groups`
scope, so no behaviour change to existing apps until they opt in.
Validated with `tofu validate`.
authentik_provider_oauth2.allowed_redirect_uris is list(map(string)) and the
API always stores a redirect_uri_type key on each entry. The module only set
matching_mode and url, so every plan showed the Grafana provider being updated
in-place (state map had 3 keys, config map had 2) and never converged.
Add redirect_uri_type to the redirect_uris object, defaulting to
"authorization". `terragrunt plan` now reports no changes.
Adding the first managed group (argocd-admins) exposed a dormant bug: the
groups resource resolved `parents` by indexing authentik_group.this itself,
which OpenTofu rejects as a self-referential block. config/groups/ had been
empty, so `tofu plan` never hit it before.
Pass `parents` through as literal group PKs instead (the resource cannot
reference itself, so parent-by-map-key was never viable). Plan is clean:
3 to add (argocd provider, application, argocd-admins group).
Adds an OIDC provider + application so the in-cluster Grafana
(grafana.k8s.syd1.au.unkin.net) can authenticate users against Authentik.
Extends the oauth2 module so provider config stays declarative and
secret-free:
- Resolve authorization/invalidation flows by slug (data.authentik_flow)
and scope mappings by managed identifier
(data.authentik_property_mapping_provider_scope).
- Read client_secret from Vault kv-v2 (data.vault_kv_secret_v2) instead of
committing it; adds the hashicorp/vault provider (auth via VAULT_ADDR/
VAULT_TOKEN from the Makefile).
- Support allowed_redirect_uris on the oauth2 provider.
config/providers_oauth2/grafana.yaml wires client_id `grafana`, the
openid/email/profile scopes, the login/generic_oauth redirect URI, and
points client_secret at kv/kubernetes/namespace/grafana/default/oauth-credentials.
- Terraform module for groups, SAML/OAuth2/LDAP providers, applications, and LDAP outposts
- Data-driven YAML config with Terragrunt config loader
- Environment: identity.unkin.net with Consul backend
- Provider: goauthentik/authentik 2026.5.0
- Woodpecker CI pipelines (pre-commit, plan, apply)
- Makefile with Vault AppRole and K8s auth support