package flow import ( "html" "net/http" "git.lohmar.co.uk/cclohmar/NextMyDay/internal/core/web" ) // renderHTML writes a server-rendered page inside the core shell (SPEC-009 §4). // The door renders no dynamic HTML itself (SPEC-003 §4); these are the flow's // hand-off targets. // // F-2: the enrollment screen carries the TOTP ceremony and the login screen a // session — neither may be cached, so every flow response is `no-store`. func renderHTML(w http.ResponseWriter, status int, page web.Page) error { w.Header().Set("Cache-Control", "no-store") return web.Render(w, status, page) } // enrollPage is the enrollment screen (SPEC-006 §4). The passkey ceremony is // driven by the same-origin WebAuthn client (SPEC-009 §5) from the data-* // contract on
; the TOTP forms work without JavaScript. Every form // carries the signed pre-session CSRF token (A15), which the client also reads // from the shell's . func enrollPage(guid, token string) web.Page { g := html.EscapeString(guid) c := html.EscapeString(token) action := func(name string) string { return `"/` + g + `?do=auth&action=` + name + `"` } return web.Page{ Title: "Enrollment", GUID: guid, Guest: true, CSRF: token, MainAttrs: web.Attrs(`data-webauthn="enroll"` + ` data-begin=` + action("enroll.begin") + ` data-finish=` + action("enroll.finish")), Content: web.Markup(`

Enrollment

Two factors are required: a passkey and an authenticator app.

1. Passkey

Register a passkey for this device.

2. Authenticator app

`), } } // passkeyPage is the post-login passkey nudge (SPEC-006 §8, D-041). The // WebAuthn registration is begun server-side and the options are embedded for // the browser; the single `passkey.add` action completes it. The two skip forms // work without JavaScript. Every form carries the SESSION-bound CSRF token (a // session exists here — A15 does not apply). func passkeyPage(guid, token, options string) web.Page { g := html.EscapeString(guid) c := html.EscapeString(token) action := func(name string) string { return `"/` + g + `?do=auth&action=` + name + `"` } return web.Page{ Title: "Add a passkey", GUID: guid, CSRF: token, Flash: "Skip the one-time code next time by adding a passkey for this device.", MainAttrs: web.Attrs(`data-webauthn="passkey"` + ` data-finish=` + action("passkey.add") + ` data-options="` + html.EscapeString(options) + `"`), Content: web.Markup(`

Add a passkey

Use this device's screen lock or security key.

Not now?

`), } } // loginPage is the login screen (SPEC-006 §5): Passkey primary, TOTP fallback. func loginPage(guid, token string) web.Page { g := html.EscapeString(guid) c := html.EscapeString(token) action := func(name string) string { return `"/` + g + `?do=auth&action=` + name + `"` } return web.Page{ Title: "Sign in", GUID: guid, Guest: true, CSRF: token, MainAttrs: web.Attrs(`data-webauthn="login"` + ` data-begin=` + action("login.begin") + ` data-finish=` + action("login.finish")), Content: web.Markup(`

Sign in

Use a passkey for this device, or your authenticator code.

Passkey

Sign in with a passkey.

Authenticator code

`), } }