7.0 KiB
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_credentialstable (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.