fix(k8s-dashboard): use email_verified=true + groups scope mappings

The apiserver rejects the email username-claim when email_verified is false
(invalid bearer token 401). Authentik external/social users are unverified,
so the default scope-email mapping fails. Mirror the proven kubernetes
provider: use the custom 'Kubernetes Email (verified)' mapping (hardcodes
email_verified=true) + 'Kubernetes Groups'. Drop the now-unneeded dual-aud
mapping (apiserver trusts the k8s-dashboard issuer w/ audience=client_id) and
align oauth2-proxy scope to 'openid email profile groups'.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Viktor Barzin 2026-06-04 03:23:59 +00:00
parent 1042c0f082
commit cb96d5d590
2 changed files with 28 additions and 24 deletions

View file

@ -43,25 +43,28 @@ data "authentik_certificate_key_pair" "signing" {
name = "authentik Self-signed Certificate" name = "authentik Self-signed Certificate"
} }
# Default OIDC scope mappings. `profile` carries the `groups` claim in # Scope mappings MIRROR the proven `kubernetes` provider exactly. Two are
# Authentik's default expression, which the apiserver reads via # custom (no `managed` field) and are looked up by name:
# --oidc-groups-claim=groups. offline_access enables refresh tokens. # * "Kubernetes Email (verified)" hardcodes `email_verified: true`. REQUIRED:
data "authentik_property_mapping_provider_scope" "defaults" { # the apiserver rejects the email username-claim when email_verified is
managed_list = [ # false (Authentik external/social users are unverified), so the default
"goauthentik.io/providers/oauth2/scope-openid", # `scope-email` mapping (which passes through the real false) yields
"goauthentik.io/providers/oauth2/scope-email", # "invalid bearer token" 401s. This custom mapping is why the CLI works.
"goauthentik.io/providers/oauth2/scope-profile", # * "Kubernetes Groups" emits the `groups` claim (scope_name=groups), so the
"goauthentik.io/providers/oauth2/scope-offline_access", # client must request the `groups` scope (see oauth2_proxy.tf).
] # The token `aud` defaults to the client_id (`k8s-dashboard`), which the
# apiserver's k8s-dashboard issuer trusts no custom audience mapping needed.
data "authentik_property_mapping_provider_scope" "openid" {
managed = "goauthentik.io/providers/oauth2/scope-openid"
} }
data "authentik_property_mapping_provider_scope" "profile" {
# Custom scope mapping that overrides the audience. It only fires when the managed = "goauthentik.io/providers/oauth2/scope-profile"
# client REQUESTS this scope, so oauth2-proxy must include }
# `k8s-dashboard-audience` in its --scope (see oauth2_proxy.tf). data "authentik_property_mapping_provider_scope" "email_verified" {
resource "authentik_property_mapping_provider_scope" "k8s_dashboard_aud" { name = "Kubernetes Email (verified)"
name = "k8s-dashboard audience" }
scope_name = "k8s-dashboard-audience" data "authentik_property_mapping_provider_scope" "groups" {
expression = "return {\"aud\": [\"kubernetes\", \"k8s-dashboard\"]}" name = "Kubernetes Groups"
} }
resource "authentik_provider_oauth2" "k8s_dashboard" { resource "authentik_provider_oauth2" "k8s_dashboard" {
@ -85,10 +88,12 @@ resource "authentik_provider_oauth2" "k8s_dashboard" {
include_claims_in_id_token = true include_claims_in_id_token = true
signing_key = data.authentik_certificate_key_pair.signing.id signing_key = data.authentik_certificate_key_pair.signing.id
property_mappings = concat( property_mappings = [
data.authentik_property_mapping_provider_scope.defaults.ids, data.authentik_property_mapping_provider_scope.openid.id,
[authentik_property_mapping_provider_scope.k8s_dashboard_aud.id], data.authentik_property_mapping_provider_scope.profile.id,
) data.authentik_property_mapping_provider_scope.email_verified.id,
data.authentik_property_mapping_provider_scope.groups.id,
]
} }
resource "authentik_application" "k8s_dashboard" { resource "authentik_application" "k8s_dashboard" {

View file

@ -53,8 +53,7 @@ resource "kubernetes_deployment" "oauth2_proxy" {
"--redirect-url=https://k8s.viktorbarzin.me/oauth2/callback", "--redirect-url=https://k8s.viktorbarzin.me/oauth2/callback",
"--upstream=${local.oauth2_proxy_upstream}", "--upstream=${local.oauth2_proxy_upstream}",
"--ssl-upstream-insecure-skip-verify=true", "--ssl-upstream-insecure-skip-verify=true",
"--scope=openid email profile offline_access k8s-dashboard-audience", "--scope=openid email profile groups",
"--oidc-extra-audience=kubernetes",
"--pass-authorization-header=true", "--pass-authorization-header=true",
"--set-authorization-header=true", "--set-authorization-header=true",
"--pass-access-token=true", "--pass-access-token=true",