feat(auth): per-instance opt-out for the email_verified strict check (#115) #117
No reviewers
Labels
No labels
area:auth
area:ci
area:db
area:infra
area:native
area:pwa
area:service
epic
feature
foundation
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
james/carol!117
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "115-oidc-email-verified-optional"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
OIDC_<NAME>_REQUIRE_EMAIL_VERIFIED=falsedisables the strictemailVerified === truecheck for one OIDC instance. Default istrue(current behaviour). Only the literal stringfalseopts out —"False","no","0","off", empty, and unset all stay on the secure default. Anti-typo defence so a misconfiguration can't silently weaken the check.Intended for self-hosters with complete control over their IdP's user population — admin-curated Authentik for a known team, etc. Re-opens the account-takeover-via-unverified-email vector from ADR-0015 §3 on instances with self-service registration or social-login bridges, so the security trade-off is documented prominently in both ADR-0017 §4.6 and the self-hoster guide.
What stays enforced regardless of the flag
emailclaim itself — Carol's linking decision tree needs it, with or without the verified-flag policy.email_in_use/identity_belongs_to_otherrefusals from ADR-0015 §3 still fire.Visibility
The flag emits a single loud
console.warnthe first time the instance is built per process:A module-level
Set<string>tracks which instances we've shouted about;parseConfigsruns on everygetEnabledProviders()call so a naive per-call warn would spam container logs. Reset hook (_clearWarnedInstancesForTests) for the test surface.Userinfo fallback tightening
Bonus: the existing userinfo fallback now only round-trips for
email_verifiedwhen the instance is configured to check it AND the id_token didn't carry it. Saves one request per sign-in in the opt-out case.Test plan
npm run typecheck— clean.npm run lint— clean.npm test— 281 / 50 skipped (was 273 / 50; 8 new tests).tests/auth/oidc-providers.test.ts(+4): warn fires on literal"false"; deduplicates across calls; typo values ("False","no","0","off","") do NOT warn; default (unset) does not warn.tests/api/oauth.test.ts(+4): id_tokenemail_verified: false+ opt-out → session; id_token without claim + opt-out → session (no userinfo round-trip needed); STILL rejects whenemailitself is missing even with opt-out;"False"(capital F) leaves the strict check on and rejects.carol.int.wynning.techonce released — setOIDC_AUTHENTIK_REQUIRE_EMAIL_VERIFIED=false, observe the warn in startup logs, sign in as a user whoseemail_verifiedis false.Files
lib/auth/oidc-providers.ts— env parsing +InstanceConfig.requireEmailVerified+ warn-once + conditional check.docs/adr/0017-oidc-generic-provider.md— new §4.6.docs/oidc-self-hoster-guide.md— new "Foot-gun" section with the threat model up front.tests/auth/oidc-providers.test.ts,tests/api/oauth.test.ts— new scenarios.Closes #115.
🤖 Generated with Claude Code
📊 Test coverage
Patch coverage: 100.0% (24/24 added lines) ✅ (soft target ≥ 80%)
Overall (
app/,lib/,db/, excluding UI per ADR-0019):Changed files in this PR (source only — tests excluded):
lib/auth/oidc-providers.tsSoft thresholds per ADR-0019. Coverage is informational and does not block merge.