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(`