fix(mealie): restore OIDC with scoped verified email #158

Merged
morten-olsen merged 2 commits from fix/842a05bd-mealie-login into main 2026-10-15 22:30:13 +02:00
Owner

Summary

Mealie rejects Morten's successful Authentik login because its provider attaches two email mappings with opposing verification claims. Request a unique mealie-email scope instead, using the existing operator's scope selection:

active user 7 + username morten + email morten@olsen.pro → email_verified=true
all other identities / changed email                 → email_verified=false

Keep Mealie's email-verification requirement enabled and link to the existing account. Include the reviewed mapping, a check/apply provisioning tool with case-insensitive scope-collision guards matching the operator, recovery instructions and policy/render tests. Python is pinned for the existing CI chart gate. Morten approved this exact identity/address policy in task 842a05bd-9c04-4b06-a390-acf65155ebe2.

Evidence

  • Before: live OAuth callbacks returned 401 with email_verified claim is missing or false; Authentik authorization, token and userinfo requests succeeded. The provider has custom true and default false email mappings; the operator reattaches both.
  • After (local): all seven policy/provisioning tests pass; the chart gate passes dependency build, Helm lint/render, kubeconform and render tests. Server dry-run in prod accepts all rendered objects, including AuthentikClient, Istio and VolSync CRDs. The existing duplicate TZ warning is unchanged.
  • After (live): mapping provisioned, then merged as 86fee7a. Woodpecker 307 (PR) and 308 (merge) passed; mirror has no error; Mealie is Synced/Healthy at that revision. Runtime and redirect scopes align, verification is enabled, actual Authentik evaluation passes approved/negative cases, and all 15 other provider assignments are unchanged. The existing admin account and all 25 recipes remain. Morten's browser acceptance is still pending.

Merge danger

Door: two-way. Reverting scope values restores the previous login rejection; credentials and application data are untouched.

Blast radius: app. The dedicated mapping is selected only by Mealie; shared mappings and other providers remain unchanged. Mealie uses Recreate, so expect a short application interruption on rollout.

Rollback and verification

  • Rollout order: Security review and green Woodpecker chart gate, followed by reviewed mapping provisioning with --apply, then merge/reconciliation. This approved Authentik configuration recovery action created only the dedicated mapping. Its expression is tracked in Git and applied explicitly; provider selection is continuously reconciled by GitOps/operator.
  • Roll back: revert this PR through Forgejo. The unused mapping remains inert; no database rollback or credential reset.
  • Verify after rollout: mirror revision, ArgoCD revision/health, ready pod, aligned requested/provider scopes, verification still enabled, exactly one email-producing mapping, actual Authentik evaluator positive/negative cases, then Morten's browser login to the existing administrator account and recipes.
  • Reviewers: Security Engineer: approved identity attestation, expression, unique scope isolation, provisioning checks and rollback. No shared operator code, RBAC or exposure changes.
## Summary Mealie rejects Morten's successful Authentik login because its provider attaches two `email` mappings with opposing verification claims. Request a unique `mealie-email` scope instead, using the existing operator's scope selection: ```text active user 7 + username morten + email morten@olsen.pro → email_verified=true all other identities / changed email → email_verified=false ``` Keep Mealie's email-verification requirement enabled and link to the existing account. Include the reviewed mapping, a check/apply provisioning tool with case-insensitive scope-collision guards matching the operator, recovery instructions and policy/render tests. Python is pinned for the existing CI chart gate. Morten approved this exact identity/address policy in task 842a05bd-9c04-4b06-a390-acf65155ebe2. ## Evidence - **Before:** live OAuth callbacks returned 401 with `email_verified claim is missing or false`; Authentik authorization, token and userinfo requests succeeded. The provider has custom true and default false email mappings; the operator reattaches both. - **After (local):** all seven policy/provisioning tests pass; the chart gate passes dependency build, Helm lint/render, kubeconform and render tests. Server dry-run in prod accepts all rendered objects, including AuthentikClient, Istio and VolSync CRDs. The existing duplicate TZ warning is unchanged. - **After (live):** mapping provisioned, then merged as 86fee7a. Woodpecker 307 (PR) and 308 (merge) passed; mirror has no error; Mealie is Synced/Healthy at that revision. Runtime and redirect scopes align, verification is enabled, actual Authentik evaluation passes approved/negative cases, and all 15 other provider assignments are unchanged. The existing admin account and all 25 recipes remain. Morten's browser acceptance is still pending. ## Merge danger **Door:** two-way. Reverting scope values restores the previous login rejection; credentials and application data are untouched. **Blast radius:** app. The dedicated mapping is selected only by Mealie; shared mappings and other providers remain unchanged. Mealie uses Recreate, so expect a short application interruption on rollout. ## Rollback and verification - **Rollout order:** Security review and green Woodpecker chart gate, followed by reviewed mapping provisioning with `--apply`, then merge/reconciliation. This approved Authentik configuration recovery action created only the dedicated mapping. Its expression is tracked in Git and applied explicitly; provider selection is continuously reconciled by GitOps/operator. - **Roll back:** revert this PR through Forgejo. The unused mapping remains inert; no database rollback or credential reset. - **Verify after rollout:** mirror revision, ArgoCD revision/health, ready pod, aligned requested/provider scopes, verification still enabled, exactly one email-producing mapping, actual Authentik evaluator positive/negative cases, then Morten's browser login to the existing administrator account and recipes. - **Reviewers:** Security Engineer: approved identity attestation, expression, unique scope isolation, provisioning checks and rollback. No shared operator code, RBAC or exposure changes.
fix(mealie): scope verified email to approved identity
All checks were successful
ci/woodpecker/pr/charts Pipeline was successful
9ea6d31470
morten-olsen left a comment

Reviewed homelab/apps#158 at 9ea6d31470 against the approved parent-task identity policy. Standards [blocking, MEDIUM] apps/charts/mealie/scripts/reconcile-email-mapping.py: candidates checks Q(scope_name=settings["scope"]), but Authentik operator internal/authentik/client.go:GetScopeMappings lowercases both requested and stored scope names. A differently cased mealie-email mapping is invisible to check/apply yet also selected by the operator, defeating the promised sole-mapping guard. Use scope_name__iexact (including collision rejection when the stored spelling differs), and add a regression test for a differently cased conflicting mapping. Check all case variants live before provisioning. Verified selector mismatch in source; existence of such a live collision is UNKNOWN, and claim overwrite/exploitability is not established. This is fixable in the provisioning guard. Checked: diff, repo instructions, common chart template and actual Helm output with ApplicationSet globals; five policy tests and render assertions pass. Forgejo commit status confirms Woodpecker 306 success. No new RBAC, image source, privilege, exposure, Secret payload, shared mapping or provider mutation is introduced. Check mode enforces a read-only transaction; apply rolls back errors atomically, rejects managed/shared mappings, and suppresses startup logs. Spec [non-blocking] Exact active user 7 + username morten + exact address attestation matches Morten's recorded approval; changed identity/address and inactive users return false. Email and email_verified are emitted; openid/profile are retained and application/provider scopes align. Verification remains explicitly true. No password reset or migration. Provision-before-merge ordering and revert-to-previous-scopes rollback are documented; unused mapping may remain inert. Live: kubectl get deployment -n prod mealie reports availableReplicas=1; application mealie is Synced/Healthy at f4503f4 (pre-change). AuthentikClient reads are RBAC-forbidden; no exec, logs, Secrets or ConfigMaps read. Actual Authentik evaluator, provider attachments/profile claims and Mealie browser login remain owner rollout checks. Before rollout confirm one email-producing mapping across case-insensitive scope matches, then test positive/negative claims with Authentik's evaluator; after reconciliation verify live scopes and verification requirement. Morten must confirm existing admin account/recipes. Rollback restores the diagnosed rejection without credential/data changes. Standards: 1 blocking finding, worst MEDIUM. Spec: 0 blocking findings; rollout verification remains non-blocking for this pre-rollout review.

Reviewed homelab/apps#158 at 9ea6d31470824520e0447443a1edb1f442761e98 against the approved parent-task identity policy. Standards [blocking, MEDIUM] apps/charts/mealie/scripts/reconcile-email-mapping.py: candidates checks Q(scope_name=settings["scope"]), but Authentik operator internal/authentik/client.go:GetScopeMappings lowercases both requested and stored scope names. A differently cased mealie-email mapping is invisible to check/apply yet also selected by the operator, defeating the promised sole-mapping guard. Use scope_name__iexact (including collision rejection when the stored spelling differs), and add a regression test for a differently cased conflicting mapping. Check all case variants live before provisioning. Verified selector mismatch in source; existence of such a live collision is UNKNOWN, and claim overwrite/exploitability is not established. This is fixable in the provisioning guard. Checked: diff, repo instructions, common chart template and actual Helm output with ApplicationSet globals; five policy tests and render assertions pass. Forgejo commit status confirms Woodpecker 306 success. No new RBAC, image source, privilege, exposure, Secret payload, shared mapping or provider mutation is introduced. Check mode enforces a read-only transaction; apply rolls back errors atomically, rejects managed/shared mappings, and suppresses startup logs. Spec [non-blocking] Exact active user 7 + username morten + exact address attestation matches Morten's recorded approval; changed identity/address and inactive users return false. Email and email_verified are emitted; openid/profile are retained and application/provider scopes align. Verification remains explicitly true. No password reset or migration. Provision-before-merge ordering and revert-to-previous-scopes rollback are documented; unused mapping may remain inert. Live: kubectl get deployment -n prod mealie reports availableReplicas=1; application mealie is Synced/Healthy at f4503f4 (pre-change). AuthentikClient reads are RBAC-forbidden; no exec, logs, Secrets or ConfigMaps read. Actual Authentik evaluator, provider attachments/profile claims and Mealie browser login remain owner rollout checks. Before rollout confirm one email-producing mapping across case-insensitive scope matches, then test positive/negative claims with Authentik's evaluator; after reconciliation verify live scopes and verification requirement. Morten must confirm existing admin account/recipes. Rollback restores the diagnosed rejection without credential/data changes. Standards: 1 blocking finding, worst MEDIUM. Spec: 0 blocking findings; rollout verification remains non-blocking for this pre-rollout review.
fix(mealie): reject case-insensitive scope collisions
All checks were successful
ci/woodpecker/pr/charts Pipeline was successful
30e34d3d89
morten-olsen left a comment

Targeted re-review of 9ea6d31..30e34d3d895e6b48879f4afe981bed5eecb88605 on PR #158. Standards [non-blocking, resolved] The earlier MEDIUM blocking scope-collision finding is resolved: scope_name__iexact now detects the case variants selected by the operator, while the existing exact spelling guard rejects a noncanonical mapping. Regression tests execute the remote provisioning code with uppercase/mixed-case collisions and exact-plus-uppercase duplicates, in both check/apply modes, asserting no mapping creation or save. Independently ran all seven tests: PASS. Forgejo status for this exact commit confirms Woodpecker 307 SUCCESS. Spec [non-blocking] The fix is limited to the collision guard, regression coverage and documentation. Approved identity policy and rollout/rollback plan are unchanged. Owner reports a live READ ONLY case-insensitive query returned [] and check mode state=missing; this live Authentik query was not independently repeated because this role cannot exec/read that configuration. Owner also supplied positive/negative evaluation evidence from running Authentik. No blocking findings remain in the reviewed change. This clears the Security review gate for the already approved scoped recovery; Software Engineer retains provisioning, merge and rollout ownership. Provision mapping before scope rollout, verify sole selected email mapping and aligned live scopes/verification, and retain Morten's existing-admin-account/recipes browser check. Rollback remains reverting chart scopes, restoring the prior rejection without data/credential changes. Standards: 0 open findings (previous blocking MEDIUM resolved). Spec: 0 blocking findings; live rollout/browser acceptance remains outstanding.

Targeted re-review of 9ea6d31..30e34d3d895e6b48879f4afe981bed5eecb88605 on PR #158. Standards [non-blocking, resolved] The earlier MEDIUM blocking scope-collision finding is resolved: scope_name__iexact now detects the case variants selected by the operator, while the existing exact spelling guard rejects a noncanonical mapping. Regression tests execute the remote provisioning code with uppercase/mixed-case collisions and exact-plus-uppercase duplicates, in both check/apply modes, asserting no mapping creation or save. Independently ran all seven tests: PASS. Forgejo status for this exact commit confirms Woodpecker 307 SUCCESS. Spec [non-blocking] The fix is limited to the collision guard, regression coverage and documentation. Approved identity policy and rollout/rollback plan are unchanged. Owner reports a live READ ONLY case-insensitive query returned [] and check mode state=missing; this live Authentik query was not independently repeated because this role cannot exec/read that configuration. Owner also supplied positive/negative evaluation evidence from running Authentik. No blocking findings remain in the reviewed change. This clears the Security review gate for the already approved scoped recovery; Software Engineer retains provisioning, merge and rollout ownership. Provision mapping before scope rollout, verify sole selected email mapping and aligned live scopes/verification, and retain Morten's existing-admin-account/recipes browser check. Rollback remains reverting chart scopes, restoring the prior rejection without data/credential changes. Standards: 0 open findings (previous blocking MEDIUM resolved). Spec: 0 blocking findings; live rollout/browser acceptance remains outstanding.
Sign in to join this conversation.
No description provided.