NextMyDay/internal/core/server/authflow.go
Claus Lohmar d11e14295b feat(core): add the store, identity plumbing and the door (SPEC-003/004)
- store: SQLite (WAL, foreign keys ON, CGO-free driver) + the embedded
  migration runner; each module's migrations are name-prefixed (D-012)
- audit: the append-only audit log; the actor threads via context (R-1)
- users: the user_id/GUID/email anchors (D-013)
- config: the YAML settings file (D-035) + validation
- httpx: cookies · loopback detection · trusted-proxy XFF · CSRF · security
  headers · redaction
- middleware: correlation id · audit · rate limiting
- mail: the invite/recovery mailer seam
- server: the core DOOR — the resolution switch, the dispatch registry
  (RegisterModule), the role gates, /setup · enroll/login · recovery flows,
  the reserved `static` workflow, and the loopback-only bootstrap (D-025)
2026-10-14 10:53:51 +00:00

49 lines
2.2 KiB
Go

package server
import (
"net/http"
"git.lohmar.co.uk/cclohmar/NextMyDay/internal/core/auth/flow"
)
// authModule is the `do` module name carrying the SPEC-006 enroll/login
// actions. The GUID still rides the path — `do`/`action` never carry identity
// (D-032/D-039).
const authModule = "auth"
// RegisterAuthFlow registers the SPEC-006 core screens (enroll · login) and the
// `auth` action module into reg. It is the wiring the door needs to make
// `POST /{GUID}?do=auth&action=…` resolve and dispatch.
func RegisterAuthFlow(reg *MapRegistry, a *flow.Actions) {
reg.RegisterCore("enroll", adaptFlow(a.EnrollScreen))
reg.RegisterCore("login", adaptFlow(a.LoginScreen))
reg.RegisterModule(NewModule(authModule).
// SPEC-006 §8 (D-041): the session-gated passkey-nudge screen. It is a
// ROUTE on the auth module (GET /{GUID}/auth/passkey) — the redirect
// target of a nudged TOTP login.
AddRoute("passkey", "passkey.prompt", adaptFlow(a.PasskeyPrompt)).
AddAction("enroll.begin", "enroll.begin", adaptFlow(a.EnrollBegin)).
AddAction("enroll.finish", "enroll.finish", adaptFlow(a.EnrollFinish)).
AddAction("totp.begin", "totp.begin", adaptFlow(a.TOTPBegin)).
AddAction("totp.verify", "totp.verify", adaptFlow(a.TOTPVerify)).
AddAction("login.begin", "login.begin", adaptFlow(a.LoginBegin)).
AddAction("login.finish", "login.finish", adaptFlow(a.LoginFinish)).
AddAction("logout", "logout", adaptFlow(a.Logout)).
// The nudge actions carry a session, so the door session-gates them
// (serveAuthAction forwards the `passkey.` prefix to serveGUID).
AddAction("passkey.add", "passkey.add", adaptFlow(a.PasskeyAdd)).
AddAction("passkey.later", "passkey.later", adaptFlow(a.PasskeyLater)).
AddAction("passkey.never", "passkey.never", adaptFlow(a.PasskeyNever)))
}
// flowFunc is a flow handler: the GUID comes from the RequestContext (the door
// resolves it from the path; the flow never parses a raw path).
type flowFunc func(w http.ResponseWriter, r *http.Request, guid string) error
// adaptFlow adapts a flow handler to the door's Handler signature.
func adaptFlow(f flowFunc) Handler {
return func(w http.ResponseWriter, r *http.Request, rc *RequestContext) error {
return f(w, r, rc.Workflow)
}
}