Switch Mealie OIDC to confidential client

Mealie requires OIDC_CLIENT_SECRET even though its docs say "public
client with PKCE". The token exchange happens server-side in Mealie's
Python backend, so the secret never reaches the browser.

- Generate client secret, store in 1Password
- Add to Authentik external-secret and worker env
- Switch blueprint from public to confidential
- Add ExternalSecret for mealie namespace
- Update docs to reflect confidential client

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erich Blume 2026-03-16 21:50:34 -07:00
commit b0023fef92
7 changed files with 39 additions and 4 deletions

View file

@ -353,7 +353,7 @@ data:
labels: labels:
blueprints.goauthentik.io/description: "Mealie OIDC provider and application" blueprints.goauthentik.io/description: "Mealie OIDC provider and application"
entries: entries:
# OAuth2 provider for Mealie (public client — Mealie uses PKCE) # OAuth2 provider for Mealie (confidential — Mealie requires client_secret)
- model: authentik_providers_oauth2.oauth2provider - model: authentik_providers_oauth2.oauth2provider
id: mealie-provider id: mealie-provider
identifiers: identifiers:
@ -362,8 +362,9 @@ data:
name: Mealie name: Mealie
authorization_flow: !Find [authentik_flows.flow, [slug, default-provider-authorization-implicit-consent]] authorization_flow: !Find [authentik_flows.flow, [slug, default-provider-authorization-implicit-consent]]
invalidation_flow: !Find [authentik_flows.flow, [slug, default-provider-invalidation-flow]] invalidation_flow: !Find [authentik_flows.flow, [slug, default-provider-invalidation-flow]]
client_type: public client_type: confidential
client_id: mealie client_id: mealie
client_secret: !Env AUTHENTIK_MEALIE_CLIENT_SECRET
redirect_uris: redirect_uris:
- matching_mode: strict - matching_mode: strict
url: https://meals.ops.eblu.me/login url: https://meals.ops.eblu.me/login

View file

@ -78,6 +78,11 @@ spec:
secretKeyRef: secretKeyRef:
name: authentik-config name: authentik-config
key: argocd-client-secret key: argocd-client-secret
- name: AUTHENTIK_MEALIE_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: authentik-config
key: mealie-client-secret
volumeMounts: volumeMounts:
- name: blueprints - name: blueprints
mountPath: /blueprints/custom mountPath: /blueprints/custom

View file

@ -57,3 +57,7 @@ spec:
remoteRef: remoteRef:
key: "Authentik (blumeops)" key: "Authentik (blumeops)"
property: argocd-client-secret property: argocd-client-secret
- secretKey: mealie-client-secret
remoteRef:
key: "Authentik (blumeops)"
property: mealie-client-secret

View file

@ -36,6 +36,11 @@ spec:
value: "https://authentik.ops.eblu.me/application/o/mealie/.well-known/openid-configuration" value: "https://authentik.ops.eblu.me/application/o/mealie/.well-known/openid-configuration"
- name: OIDC_CLIENT_ID - name: OIDC_CLIENT_ID
value: "mealie" value: "mealie"
- name: OIDC_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: mealie-oidc
key: client-secret
- name: OIDC_AUTO_REDIRECT - name: OIDC_AUTO_REDIRECT
value: "false" value: "false"
- name: OIDC_PROVIDER_NAME - name: OIDC_PROVIDER_NAME

View file

@ -0,0 +1,19 @@
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: mealie-oidc
namespace: mealie
spec:
refreshInterval: 1h
secretStoreRef:
kind: ClusterSecretStore
name: onepassword-blumeops
target:
name: mealie-oidc
creationPolicy: Owner
data:
- secretKey: client-secret
remoteRef:
key: "Authentik (blumeops)"
property: mealie-client-secret

View file

@ -8,6 +8,7 @@ resources:
- service.yaml - service.yaml
- pvc.yaml - pvc.yaml
- ingress-tailscale.yaml - ingress-tailscale.yaml
- external-secret.yaml
images: images:
- name: registry.ops.eblu.me/blumeops/mealie - name: registry.ops.eblu.me/blumeops/mealie

View file

@ -30,11 +30,11 @@ Self-hosted recipe manager with a REST API. Part of the meal planning pipeline:
- Built-in meal planning and shopping lists - Built-in meal planning and shopping lists
- Recipe import from URLs - Recipe import from URLs
- API token auth for automation - API token auth for automation
- OIDC login via [[authentik]] (public client with PKCE) - OIDC login via [[authentik]] (confidential client)
## Authentication ## Authentication
OIDC via [[authentik]] using a public client with PKCE (no client secret needed). All Authentik users can log in; members of the `admins` group get Mealie admin privileges via `OIDC_ADMIN_GROUP`. OIDC via [[authentik]] using a confidential client. Client secret stored in 1Password (`Authentik (blumeops)` / `mealie-client-secret`) and delivered via ExternalSecret. All Authentik users can log in; members of the `admins` group get Mealie admin privileges via `OIDC_ADMIN_GROUP`.
## Storage ## Storage