162 lines
7.0 KiB
Markdown
162 lines
7.0 KiB
Markdown
# 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.
|