6d224861 came from a --no-checkout worktree whose empty index made the
commit drop every file except two. This restores 05b50d2b's full tree and
correctly adds stacks/stem95su/gdrive-sync.tf + the service-catalog stem95su
entry. Forward-only (parent=6d224861, no force-push); [ci skip] since the
live infra was never applied from the broken commit.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
10 KiB
10 KiB
| name | description | author | version | date |
|---|---|---|---|---|
| authentik | Manage the Authentik identity provider via its REST API. Use when: (1) User asks to create, update, or delete users in Authentik, (2) User asks to manage groups or group memberships, (3) User asks to create a new OAuth2/OIDC application or provider, (4) User asks to protect a service with forward auth (Authentik + Traefik), (5) User asks about SSO, single sign-on, authentication, or identity, (6) User asks to manage Authentik flows, stages, or policies, (7) User asks to configure social login (Google, GitHub, Facebook), (8) User asks about OIDC for Kubernetes or who has access to what, (9) User deploys a new service that needs authentication. Authentik v2025.10.3 running in Kubernetes, managed via REST API. | Claude Code | 1.0.0 | 2026-02-17 |
Authentik Identity Provider Management
Overview
- URL:
https://authentik.viktorbarzin.me - Admin UI:
https://authentik.viktorbarzin.me/if/admin/ - API Base:
https://authentik.viktorbarzin.me/api/v3/ - API Docs:
https://authentik.viktorbarzin.me/api/v3/docs/ - Helm Chart: authentik v2025.10.3
- Namespace:
authentik
API Access
Getting the Token
The API token is stored in terraform.tfvars (git-crypt encrypted):
AUTHENTIK_TOKEN=$(grep authentik_api_token terraform.tfvars | cut -d'"' -f2)
Making API Calls
# Generic pattern
curl -s -H "Authorization: Bearer $AUTHENTIK_TOKEN" \
"https://authentik.viktorbarzin.me/api/v3/<endpoint>/"
# With JSON body (POST/PATCH/PUT)
curl -s -X POST \
-H "Authorization: Bearer $AUTHENTIK_TOKEN" \
-H "Content-Type: application/json" \
"https://authentik.viktorbarzin.me/api/v3/<endpoint>/" \
-d '{"key": "value"}'
Verify Token Works
curl -s -H "Authorization: Bearer $AUTHENTIK_TOKEN" \
"https://authentik.viktorbarzin.me/api/v3/core/users/me/" | python3 -m json.tool
Key API Endpoints
| Endpoint | Methods | Purpose |
|---|---|---|
core/users/ |
GET, POST | List/create users |
core/users/{id}/ |
GET, PATCH, DELETE | Get/update/delete user |
core/groups/ |
GET, POST | List/create groups |
core/groups/{pk}/ |
GET, PATCH, DELETE | Get/update/delete group |
core/applications/ |
GET, POST | List/create applications |
core/tokens/ |
GET, POST | List/create tokens |
core/tokens/{identifier}/view_key/ |
GET | View token secret key |
providers/all/ |
GET | List all providers |
providers/oauth2/ |
GET, POST | OAuth2/OIDC providers |
providers/proxy/ |
GET, POST | Proxy providers (forward auth) |
flows/instances/ |
GET | List flows |
stages/all/ |
GET | List stages |
sources/all/ |
GET | List sources (social login) |
outposts/instances/ |
GET | List outposts |
propertymappings/provider/scope/ |
GET, POST | OIDC scope mappings |
rbac/roles/ |
GET | List roles |
Common Operations
List All Users
curl -s -H "Authorization: Bearer $AUTHENTIK_TOKEN" \
"https://authentik.viktorbarzin.me/api/v3/core/users/?page_size=50" | \
python3 -c "
import json,sys
for u in json.load(sys.stdin)['results']:
groups=[g['name'] for g in u.get('groups_obj',[])]
print(f\" {u['username']:<40} {u['name']:<30} groups={groups}\")
"
Create a New User
curl -s -X POST \
-H "Authorization: Bearer $AUTHENTIK_TOKEN" \
-H "Content-Type: application/json" \
"https://authentik.viktorbarzin.me/api/v3/core/users/" \
-d '{
"username": "user@example.com",
"name": "Full Name",
"email": "user@example.com",
"is_active": true,
"type": "internal",
"path": "users"
}'
Add User to Group
# First get the group to find current users
GROUP_PK="<group-uuid>"
CURRENT_USERS=$(curl -s -H "Authorization: Bearer $AUTHENTIK_TOKEN" \
"https://authentik.viktorbarzin.me/api/v3/core/groups/$GROUP_PK/" | \
python3 -c "import json,sys; print(json.load(sys.stdin)['users'])")
# Then PATCH with the updated user list (add new user pk)
curl -s -X PATCH \
-H "Authorization: Bearer $AUTHENTIK_TOKEN" \
-H "Content-Type: application/json" \
"https://authentik.viktorbarzin.me/api/v3/core/groups/$GROUP_PK/" \
-d '{"users": [<existing_pks>, <new_pk>]}'
Create a New Group
curl -s -X POST \
-H "Authorization: Bearer $AUTHENTIK_TOKEN" \
-H "Content-Type: application/json" \
"https://authentik.viktorbarzin.me/api/v3/core/groups/" \
-d '{
"name": "My New Group",
"is_superuser": false,
"parent": "<parent-group-pk-or-null>"
}'
Create OAuth2/OIDC Application (Full Flow)
Step 1: Create the OAuth2 Provider
curl -s -X POST \
-H "Authorization: Bearer $AUTHENTIK_TOKEN" \
-H "Content-Type: application/json" \
"https://authentik.viktorbarzin.me/api/v3/providers/oauth2/" \
-d '{
"name": "Provider for myapp",
"authorization_flow": "<flow-pk>",
"invalidation_flow": "<invalidation-flow-pk>",
"client_type": "confidential",
"client_id": "<generated-or-custom>",
"client_secret": "<generated-or-custom>",
"redirect_uris": "https://myapp.viktorbarzin.me/callback",
"property_mappings": ["<scope-mapping-pks>"],
"signing_key": "<signing-key-pk>"
}'
Step 2: Create the Application
curl -s -X POST \
-H "Authorization: Bearer $AUTHENTIK_TOKEN" \
-H "Content-Type: application/json" \
"https://authentik.viktorbarzin.me/api/v3/core/applications/" \
-d '{
"name": "My App",
"slug": "myapp",
"provider": <provider-pk-from-step-1>,
"meta_launch_url": "https://myapp.viktorbarzin.me"
}'
List Applications
curl -s -H "Authorization: Bearer $AUTHENTIK_TOKEN" \
"https://authentik.viktorbarzin.me/api/v3/core/applications/?page_size=50" | \
python3 -c "
import json,sys
for a in json.load(sys.stdin)['results']:
ptype = a.get('provider_obj',{}).get('verbose_name','N/A')
print(f\" {a['name']:<30} slug={a['slug']:<25} provider={ptype}\")
"
Create a Non-Expiring API Token
# Create token
curl -s -X POST \
-H "Authorization: Bearer $AUTHENTIK_TOKEN" \
-H "Content-Type: application/json" \
"https://authentik.viktorbarzin.me/api/v3/core/tokens/" \
-d '{
"identifier": "my-token-name",
"intent": "api",
"expiring": false,
"description": "Description here"
}'
# Retrieve the key
curl -s -H "Authorization: Bearer $AUTHENTIK_TOKEN" \
"https://authentik.viktorbarzin.me/api/v3/core/tokens/my-token-name/view_key/"
Important Reference UUIDs
Authorization Flows
| Flow | Slug | Use For |
|---|---|---|
| Authorize Application (explicit consent) | default-provider-authorization-explicit-consent |
Apps that should show consent screen |
| Authorize Application (implicit consent) | default-provider-authorization-implicit-consent |
Internal/trusted apps, auto-redirect |
| Logout | default-invalidation-flow |
Invalidation/logout flow |
Common Property Mappings (OIDC Scopes)
These are the standard scope mappings used by most providers:
60e33a8c-66a2-414f-840c-b13012b4d4bd— openid1f51c659-f13b-4ad4-ba89-70458ef88e9c— email4c0bf430-7f74-4216-b9d7-23703ab544ba— profile
Login Sources
| Source | Slug | Matching Mode |
|---|---|---|
google |
identifier | |
| GitHub | github |
email_link |
facebook |
email_link |
Protecting a Service with Forward Auth
To protect a service via Authentik + Traefik forward auth:
- In the service's Terraform module, set
protected = truein theingress_factorycall - This adds the
authentik-forward-authTraefik middleware - Unauthenticated users get redirected to the Authentik login page
- After login, these headers are forwarded to the service:
X-authentik-usernameX-authentik-uidX-authentik-emailX-authentik-nameX-authentik-groups
Invitation Management
Create Invitation
curl -s -X POST \
-H "Authorization: Bearer $AUTHENTIK_TOKEN" \
-H "Content-Type: application/json" \
"https://authentik.viktorbarzin.me/api/v3/stages/invitation/invitations/" \
-d '{
"name": "invite-slug-name",
"single_use": true,
"fixed_data": {"group": "Target Group Name"},
"flow": "<invitation-enrollment-flow-pk>"
}'
# Returns PK which is the itoken
# Link: https://authentik.viktorbarzin.me/if/flow/invitation-enrollment/?itoken=<pk>
List Invitations
curl -s -H "Authorization: Bearer $AUTHENTIK_TOKEN" \
"https://authentik.viktorbarzin.me/api/v3/stages/invitation/invitations/?page_size=50"
Delete Invitation
curl -s -X DELETE -H "Authorization: Bearer $AUTHENTIK_TOKEN" \
"https://authentik.viktorbarzin.me/api/v3/stages/invitation/invitations/<pk>/"
Helper Script
Use .claude/scripts/authentik-invite.sh for invitation management:
./authentik-invite.sh create "Group Name" [--days N]
./authentik-invite.sh assign <username> "Group Name"
./authentik-invite.sh list
Important Notes
- OAuth source
enrollment_flowis set toinvitation-enrollment-- new social login users require invitation - Source updates require Django ORM (PATCH not supported on
sources/oauth/<slug>/) - Invitation
namefield must be a slug (letters, numbers, hyphens, underscores)
Gotchas
- API pagination: All list endpoints return paginated results. Use
?page_size=50or checkpagination.nextfor more pages. - Group user updates: PATCH to groups replaces the entire user list — always fetch current users first, then append.
- Provider property mappings: Must reference existing scope mapping UUIDs. Query
propertymappings/provider/scope/to find them. - Signing key for OIDC: Must assign a signing key to OAuth2 providers or JWKS endpoint returns empty
{}. - Email verified claim: Default email scope mapping sets
email_verified: False. For Kubernetes OIDC, create a custom mapping that returnsTrue. - Token identifier uniqueness: Token identifiers must be unique across the entire instance.
Notes
- Authentik is classified as DEFCON Level 1 (Critical) — handle with care
- Changes to Authentik configuration (Helm chart, PgBouncer, etc.) must go through Terraform
- API-level changes (users, groups, applications) are fine to make directly via the API
- The embedded outpost auto-discovers providers assigned to it
- See also:
ingress-factory-migrationskill for protecting services