Authentik Operations¶
Operational guide for Authentik SSO configuration, email verification, and OIDC troubleshooting.
Quick Reference¶
| Property | Value |
|---|---|
| URL | https://auth.fzymgc.house |
| Admin UI | https://auth.fzymgc.house/if/admin/ |
| User Settings | https://auth.fzymgc.house/if/user/ |
| API Docs | https://api.goauthentik.io |
| OIDC Issuer (K8s) | https://auth.fzymgc.house/application/o/kubernetes/ |
| Kubernetes Client ID | kubernetes |
| Terraform Module | tf/authentik/ |
| Vault Secret Path | secret/fzymgc-house/cluster/authentik |
Architecture Overview¶
Email Verification Flow¶
Authentik does NOT have a native email_verified field. The email_verified claim must be stored in user.attributes.email_verified and read via a custom scope mapping.
+----------------------------------------------------------------+
| Email Verification Flow |
+----------------------------------------------------------------+
| New User Enrollment: |
| 1. User fills enrollment form (prompt stage) |
| 2. User account created inactive (write stage) |
| 3. Verification email sent (email stage) |
| 4. User clicks link, email verified |
| 5. Expression policy sets user.attributes.email_verified=true |
| 6. User logged in (login stage) |
+----------------------------------------------------------------+
| OIDC Token Generation: |
| 1. User authenticates |
| 2. Custom email scope reads user.attributes.email_verified |
| 3. JWT includes email_verified claim with actual value |
+----------------------------------------------------------------+
Key Terraform Resources¶
| Resource | File | Purpose |
|---|---|---|
authentik_property_mapping_provider_scope.email_verified |
data-sources.tf |
Custom email scope with email_verified from attributes |
authentik_flow.enrollment_with_email_verification |
enrollment-flow.tf |
Enrollment flow with email verification |
authentik_stage_email.enrollment_email_verification |
enrollment-flow.tf |
Email verification stage |
authentik_policy_expression.set_email_verified |
enrollment-flow.tf |
Sets email_verified: true after verification |
User Management¶
Create User¶
- Navigate to Admin UI > Directory > Users
- Click "Create" and fill in details
- Assign to appropriate groups
Disable User¶
- Find user in Admin UI
- Set "Is Active" to false
- User immediately loses access
Password Reset¶
Users can self-service via "Forgot Password" flow if email is configured.
Group Management¶
Access Control Pattern¶
Applications use a two-tier group model with policy_engine_mode = "any" and dual policy bindings:
| Base Group | Admin Group | Application |
|---|---|---|
vault-users |
vault-admin |
Vault |
grafana-editor |
grafana-admin |
Grafana |
temporal-users |
temporal-admin |
Temporal |
mealie-users |
mealie-admins |
Mealie (groups via OIDC claims, no binding) |
paperless-users |
paperless-admin |
Paperless (no TF application) |
uptime-kuma-users |
— | Uptime Kuma (launcher only) |
Each application has policy bindings for both the base and admin groups. This grants access to admin group members directly, without relying on group parent hierarchy.
Why dual bindings? Authentik 2025.12.x has a known bug (#15159) where the
parentsM2M field silently fails to persist. Until this is fixed upstream, admin group members would not inherit base group access through hierarchy. The dual binding pattern is safe to keep even after the bug is fixed.
Core Groups¶
| Group | Access |
|---|---|
authentik Admins |
Full Authentik admin |
cluster-admins |
Kubernetes admin access |
k8s-admins |
Kubernetes OIDC admin |
OIDC Applications¶
Configured applications using OIDC:
- Kubernetes API (cluster access)
- Vault
- Grafana
Forward Auth Applications¶
Applications using Traefik forward auth with Authentik proxy providers. These apps lack native OIDC support, so authentication is handled at the reverse proxy layer via Traefik middleware.
- Temporal (
temporal.fzymgc.house) - Managed viatf/authentik/temporal.tf - Longhorn (
longhorn.fzymgc.house) - Uses embedded outpost (no dedicated provider)
Note: The embedded outpost at
/outpost.goauthentik.io/auth/traefikroutes requests to the correct application based on theHostheader matching the provider'sexternal_hostconfiguration.
Temporal Access Management¶
Temporal Web UI uses forward auth with group-based access control. Access is managed via the temporal-users and temporal-admin groups.
Groups:
| Group | Purpose |
|---|---|
temporal-users |
Gate access to Temporal Web UI |
temporal-admin |
Future admin functionality (inherits from temporal-users) |
Note: Forward auth only controls access to the UI itself. Both groups currently have identical UI access since Temporal Web UI doesn't have built-in role-based authorization. The admin group is created for future integration with Temporal's namespace-level RBAC if needed.
Grant access:
- Navigate to Authentik Admin → Directory → Groups
- Select
temporal-users(ortemporal-adminfor designated admins) - Add user to group membership
Revoke access:
Remove user from temporal-users group. Access is revoked on next request (token validity is 24 hours, but the policy check happens on each auth request).
Terraform management:
Users managed in Terraform can be added to groups in tf/authentik/users.tf:
resource "authentik_user" "example" {
username = "example"
name = "Example User"
email = "example@domain.com"
groups = [
# Choose ONE (admin inherits user access via parent group):
authentik_group.temporal_users.id, # Basic access
# authentik_group.temporal_admin.id, # Admin access (includes user-level)
]
}
Common Operations¶
Setting email_verified for Existing Users¶
Warning: Direct attribute manipulation bypasses enrollment flows and should only be used for migrating existing users. New users should always go through the enrollment flow.
Existing users created before the email verification flow need manual attribute setting:
# Get Authentik token from Vault
export VAULT_ADDR=https://vault.fzymgc.house
AUTHENTIK_TOKEN=$(vault kv get -field=terraform_token secret/fzymgc-house/cluster/authentik)
# Find user ID
curl -s -H "Authorization: Bearer $AUTHENTIK_TOKEN" \
'https://auth.fzymgc.house/api/v3/core/users/?search=USERNAME' | jq '.results[0].pk'
# Set email_verified attribute (replace USER_ID)
curl -s -X PATCH \
-H "Authorization: Bearer $AUTHENTIK_TOKEN" \
-H "Content-Type: application/json" \
-d '{"attributes": {"email_verified": true}}' \
"https://auth.fzymgc.house/api/v3/core/users/USER_ID/"
Revoking User Consent¶
To force a user to re-consent to an application:
- Go to https://auth.fzymgc.house/if/user/#/settings
- Navigate to Consent section
- Select the application and click Delete
Debugging OIDC Flows¶
-
Check provider configuration:
-
List scope mappings on provider:
-
Verify scope mapping expression:
Troubleshooting¶
OIDC Authentication Returns "Unauthorized"¶
Symptom: kubectl --context fzymgc-house-oidc get nodes returns Unauthorized
Diagnosis Steps:
-
Clear OIDC cache and re-authenticate:
-
Decode the JWT to check claims:
-
Check if
email_verifiedistruein the JWT
Common Causes:
| Cause | Solution |
|---|---|
email_verified: false in JWT |
User's attributes.email_verified not set - see "Setting email_verified for Existing Users" |
| Missing groups claim | Check user group membership in Authentik |
| Token expired | Clear cache and re-authenticate |
| SSO session caching old claims | Log out of Authentik completely, then re-authenticate |
email_verified Always False¶
Root Cause: Authentik's default email scope returns email_verified: false for security reasons (see GitHub issue #16205).
Solution: Use custom email scope that reads from user.attributes.email_verified:
# Custom scope expression (in data-sources.tf)
return {
"email": request.user.email,
"email_verified": request.user.attributes.get("email_verified", False),
}
Consent Screen Not Showing Email Scope¶
Symptom: OAuth consent only shows "General Profile Information", not email
Cause: Scope mappings without a description field are treated as "silent" scopes
Solution: Add description to the scope mapping:
resource "authentik_property_mapping_provider_scope" "email_verified" {
name = "Email (with verified attribute)"
scope_name = "email"
description = "Email Address" # Required for consent display
expression = "..."
}
Terraform Resets User Attributes¶
Symptom: email_verified attribute disappears after Terraform apply
Cause: User resources managed by Terraform without ignore_changes for attributes
Solution: Add attributes to lifecycle ignore_changes:
resource "authentik_user" "example" {
# ... user config ...
lifecycle {
prevent_destroy = true
ignore_changes = [password, attributes] # Don't reset flow-managed state
}
}
OIDC Login Returns "Permission Denied"¶
Symptom: Clicking OIDC login (e.g., Vault) redirects to Authentik, which shows "Permission denied" for the user. The provider popup may close with "Authentication failed: The provider window was closed before authentication was complete."
Root Cause: The application has a policy binding for a base group (e.g., vault-users), but the user is only in the admin group (e.g., vault-admin). Authentik's group parent hierarchy (parents M2M field) is supposed to grant inherited membership, but a known bug (#15159) causes parent assignments to silently fail.
The bug is in a PostgreSQL trigger: REFRESH MATERIALIZED VIEW CONCURRENTLY on the GroupParentageNode table causes lock contention that silently rolls back M2M parent writes. The API returns 200 OK and the Terraform provider reports success, but parents are empty.
Diagnosis:
-
Check Authentik server logs (Grafana/Loki):
-
Verify group membership in Authentik Admin UI:
- Admin UI → Directory → Users → select user → Groups tab
-
Check if the user is in the base group (not just admin)
-
Verify parent hierarchy:
- Admin UI → Directory → Groups → select admin group → Update
- Check if "Parents" shows the base group (likely empty due to bug)
Fix: Add policy bindings for both the base group AND the admin group on each application. Since policy_engine_mode = "any", either binding grants access. See Group Management > Access Control Pattern above.
Affected versions: Authentik 2025.12.x (bug present since parent → parents M2M migration)
Login Failures¶
- Check user is active
- Verify group membership (including policy bindings — see above)
- Review Authentik logs in Grafana
OIDC Issues¶
- Check application configuration
- Verify redirect URIs
- Check policy bindings (see "OIDC Login Returns Permission Denied" above)
- Test with Authentik built-in debug tools