# Phase 19: Local Auth (No-OIDC Mode) - Discussion Log > **Audit trail only.** Do not use as input to planning, research, or execution agents. > Decisions are captured in CONTEXT.md — this log preserves the alternatives considered. **Date:** 2026-06-16 **Phase:** 19-local-auth-no-oidc-mode **Areas discussed:** Mode & coexistence, Session issuance, Password hashing & storage, Accounts & OIDC-link, Testing & dev-bypass --- ## Mode & Coexistence ### Q1 — How should the app decide between local-auth and OIDC? | Option | Description | Selected | |--------|-------------|----------| | app_config flag (runtime) | `auth_mode` row in app_config, set by wizard; no restart | | | Deploy-time env switch | `AUTH_MODE` env read at boot | | | Both always live | Local form + OIDC button always shown | | **User's choice:** Free-text — "Default to local and add the ability to wire OIDC in later if wanted." **Notes:** Local is the always-available default; OIDC is additive/opt-in. ### Q2 — How does the app know OIDC is wired in, and what happens to local login? | Option | Description | Selected | |--------|-------------|----------| | Auto-detect, local stays live | OIDC on when config present; local always available | (partial) | | Auto-detect, OIDC takes over | Local disabled once OIDC present | | | Explicit app_config toggle | Separate `auth_mode` controlled from admin UI | (partial) | **User's choice:** Free-text — OIDC config is set/stored in a later step, so wire it into the **admin UI**; give users the choice of which to use at login; **no local login UI exists today** so it must be built. **Notes:** Blend — admin-UI-configured OIDC, both methods offered at login, user chooses. --- ## Session Issuance ### Q1 — What backs a local login session? | Option | Description | Selected | |--------|-------------|----------| | Stateless signed JWT cookie | userId in signed httpOnly cookie; no DB table | ✓ (Claude) | | Server-side session table | sessions table for true revocation | | | You decide | — | ✓ | **User's choice:** "You decide" + "do not pigeon-hole the user into Authelia — Bring Your Own Auth and Bring Your Own CalDAV provider." **Notes:** Claude chose stateless signed JWT cookie. User added the BYO-Auth architectural principle (generic OIDC, not Authelia-locked). ### Q2 — How far should the BYO-Auth abstraction go in Phase 19? | Option | Description | Selected | |--------|-------------|----------| | Local + generic OIDC | Two concrete methods, clean seam, no framework | ✓ | | Full pluggable framework | Provider registry/plugin (LDAP, magic-link, multi-OIDC) | | | Local only for now | Leave Authelia OIDC as-is, defer generic OIDC | | **User's choice:** Local + generic OIDC (recommended). **Notes:** Clean internal seam now; full framework deferred. --- ## Password Hashing & Storage ### Q1 — Which password hashing approach? | Option | Description | Selected | |--------|-------------|----------| | scrypt via node:crypto | Stdlib, zero-dep, no native build | ✓ | | argon2id (native dep) | OWASP top pick, needs native addon | | | bcrypt (bcryptjs) | Pure JS, older KDF | | **User's choice:** scrypt via node:crypto (recommended). **Notes:** Honors the stack's no-native-dep stance. ### Q2 — Where to store username + hash? | Option | Description | Selected | |--------|-------------|----------| | Separate local_credentials table | Mirrors member_credentials; user-agnostic users row | ✓ (Claude) | | Columns on users | Add username + password_hash to users | | | You decide | — | ✓ | **User's choice:** "You decide." **Notes:** Claude chose a separate `local_credentials` table — best fits the BYO-Auth per-user-method seam (one row can hold both a local credential and an OIDC binding). --- ## Accounts & OIDC-Link ### Q1 — How are local accounts created? | Option | Description | Selected | |--------|-------------|----------| | Admin creates members | Wizard creates first admin; admin creates rest | ✓ | | Admin creates + invite link | One-time set-password link | | | Open self-signup | Anyone can register | | **User's choice:** Admin creates members. ### Q2 — Password change/reset? | Option | Description | Selected | |--------|-------------|----------| | Self-change + admin reset | Member self-change; admin resets lockouts | ✓ | | Self-change only | No admin reset | | | Admin reset only | No self-change | | **User's choice:** Self-change + admin reset. **Notes:** No email reset (email out of scope). ### Q3 — After OIDC-link, what methods stay valid? (reformulated after clarification) | Option | Description | Selected | |--------|-------------|----------| | Both stay valid | Row holds local + OIDC; either logs in | | | OIDC primary, local fallback | Same data model, UI emphasis on OIDC | | | OIDC replaces local | Linking removes local credential | ✓ (per user) | **User's choice:** Initially requested clarification; then chose **OIDC replaces local per user** — there can/should be OIDC-only users with no local creds. Raised the need for a break-glass path. **Notes:** Auth methods are per-user (presence of local_credentials row and/or OIDC binding). Break-glass need surfaced here. ### Q4 — Break-glass capability model? | Option | Description | Selected | |--------|-------------|----------| | Protected local admin (no new role model) | Initial admin, un-removable local cred | | | Operator-only account (member/operator split) | Strip member capability from break-glass | | | Let researcher scope it | Lock the requirement, defer the how | ✓ (twist) | **User's choice:** Let researcher scope it — **with a twist: break-glass can be a CLI/console command or env override instead of a user**, removing the added-user/capability complexity. **Notes:** No new role/capability model; reuse `is_admin`. Recovery mechanism (not account) to be scoped by researcher. --- ## Testing & Dev-Bypass (added mid-discussion at user's request) ### Q1 — How should DEV_AUTH_BYPASS evolve? | Option | Description | Selected | |--------|-------------|----------| | Bypass stays + seed a real test login | Fast bypass for most specs; real form for login specs | | | Replace bypass with seeded auto-login | Harness logs in via real local flow | (user's lean) | | Bypass auto-issues a real local session | Bypass logs in seeded user, skips form | | **User's choice:** Defer final determination to the **research agent**; user **leans toward "replace bypass with seeded auto-login."** **Notes:** Hard constraint — the seeded test login / dev-bypass **stays dev-only and never ships in the Docker/prod image** (Phase 16 IMG-01/02/03 gates apply). --- ## Claude's Discretion - Local session backing → stateless signed JWT cookie (D-05). - Credential storage location → separate `local_credentials` table (D-09). ## Deferred Ideas - Full pluggable auth-provider framework (registry/plugin; LDAP, magic-link, multi-OIDC) — future phase, counterpart of 999.1. - Member-vs-operator capability/role split — rejected in favor of CLI/env break-glass recovery. - Email-based password reset — out of project scope.