feat: add poimen-memory as an Authentik service-to-service client

Client credentials + device code grant, no browser redirect (empty
redirect_uris) - unlike every other SERVICES entry which is
authorization_code web SSO. First real step toward replacing
poimen-memory's static API key with a proper JWT flow.
This commit is contained in:
Story Crater Bot
2026-08-27 11:51:02 -07:00
parent e4de366d2a
commit d055483aa2
+21 -2
View File
@@ -415,6 +415,15 @@ SERVICES = {
"launch_url": "https://vault.riotpiao.com", "launch_url": "https://vault.riotpiao.com",
"display_name": "Vault", "display_name": "Vault",
}, },
"poimen-memory": {
# Service-to-service API auth (no browser redirect) - generate secret on first run.
"client_secret_source": ("poimen", "poimen-memory-oidc", "CLIENT_SECRET"),
"generate_if_missing": True,
"extra_secret_literals": {"client-id": "poimen-memory"},
"redirect_uris": [], # No browser flow, service-to-service only
"launch_url": "https://memory.riotpiao.com",
"display_name": "Poimen Memory",
},
} }
app_pks_for_binding = [] app_pks_for_binding = []
@@ -501,6 +510,16 @@ for name, cfg in SERVICES.items():
"config.json": immich_config_json, "config.json": immich_config_json,
}) })
# Service-to-service (client_credentials): poimen-memory
# Browser SSO (authorization_code): all others
grant_types = [
"urn:ietf:params:oauth:grant-type:device_code", # device code flow (CLI/headless)
"client_credentials" # service-to-service
] if name == "poimen-memory" else [
"authorization_code", # web SSO
"refresh_token" # long-lived sessions
]
provider = get_or_create( provider = get_or_create(
"/api/v3/providers/oauth2/", "/api/v3/providers/oauth2/", "/api/v3/providers/oauth2/", "/api/v3/providers/oauth2/",
f"name={name}", f"name={name}",
@@ -520,7 +539,7 @@ for name, cfg in SERVICES.items():
# every login with "Invalid grant_type for provider" -> # every login with "Invalid grant_type for provider" ->
# invalid_request. authorization_code = the web SSO flow all these # invalid_request. authorization_code = the web SSO flow all these
# apps use; refresh_token = long-lived sessions (offline_access). # apps use; refresh_token = long-lived sessions (offline_access).
"grant_types": ["authorization_code", "refresh_token"], "grant_types": grant_types,
"redirect_uris": [ "redirect_uris": [
{"matching_mode": "strict", "url": u} for u in cfg["redirect_uris"] {"matching_mode": "strict", "url": u} for u in cfg["redirect_uris"]
], ],
@@ -530,7 +549,7 @@ for name, cfg in SERVICES.items():
# truth in the k8s Secret, and re-sending it here is harmless anyway). # truth in the k8s Secret, and re-sending it here is harmless anyway).
patch_existing={ patch_existing={
"property_mappings": provider_mappings, "property_mappings": provider_mappings,
"grant_types": ["authorization_code", "refresh_token"], "grant_types": grant_types,
"redirect_uris": [ "redirect_uris": [
{"matching_mode": "strict", "url": u} for u in cfg["redirect_uris"] {"matching_mode": "strict", "url": u} for u in cfg["redirect_uris"]
], ],