feat(passkey): wire enrollment verifier into felis-api
Construct the go-webauthn verifier at the composition root and attach it to the API when auth.panel_hostname is configured (RP id = panel hostname, origin = https://<panel hostname>, display name Felis). When the hostname is unset or the verifier fails to build it stays nil and the passkey ceremony routes report 503, matching the existing nil-when-unconfigured subsystem pattern. An admin passkey, if ever added, is a separate relying party on the admin host and is intentionally not wired here.
This commit is contained in:
1 file changed
+22
@@ -14,6 +14,7 @@ import (
|
|||||||
"felis.lolicon.best/internal/build"
|
"felis.lolicon.best/internal/build"
|
||||||
"felis.lolicon.best/internal/config"
|
"felis.lolicon.best/internal/config"
|
||||||
"felis.lolicon.best/internal/panel"
|
"felis.lolicon.best/internal/panel"
|
||||||
|
"felis.lolicon.best/internal/passkey"
|
||||||
"felis.lolicon.best/internal/restore"
|
"felis.lolicon.best/internal/restore"
|
||||||
"felis.lolicon.best/internal/store"
|
"felis.lolicon.best/internal/store"
|
||||||
"felis.lolicon.best/internal/submit"
|
"felis.lolicon.best/internal/submit"
|
||||||
@@ -165,6 +166,27 @@ func cmdAPI(args []string, stdout, stderr io.Writer) int {
|
|||||||
}
|
}
|
||||||
fmt.Fprintln(stderr, "felis api: external face fails closed (Access JWKS key function not configured)")
|
fmt.Fprintln(stderr, "felis api: external face fails closed (Access JWKS key function not configured)")
|
||||||
|
|
||||||
|
// Passkey (WebAuthn) enrollment verifier (spec §14, Phase 6). The relying party is
|
||||||
|
// the panel (app) face: the RP id is the panel hostname and the single permitted
|
||||||
|
// origin is that host over https, so a credential enrolled here is scoped to the
|
||||||
|
// panel. It is wired only when auth.panel_hostname is configured; otherwise a.Passkey
|
||||||
|
// stays nil and the enrollment begin/finish routes honestly return 503 (the
|
||||||
|
// authenticated enrollment boundary is still enforced by the handlers). Scope is
|
||||||
|
// ENROLLMENT only — the login/assertion path is a deferred slice, and credentials
|
||||||
|
// enrolled under this RP id MUST be asserted under the same RP id when that slice
|
||||||
|
// lands. An admin passkey (if ever added) is a SEPARATE relying party on the admin
|
||||||
|
// host and is not wired here.
|
||||||
|
if cfg.Auth.PanelHostname != "" {
|
||||||
|
pv, err := passkey.New(cfg.Auth.PanelHostname, "Felis", []string{"https://" + cfg.Auth.PanelHostname})
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(stderr, "felis api: passkey verifier disabled: %v — passkey endpoints return 503\n", err)
|
||||||
|
} else {
|
||||||
|
a.Passkey = pv
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Fprintln(stderr, "felis api: passkey verifier disabled (auth.panel_hostname unset) — passkey endpoints return 503")
|
||||||
|
}
|
||||||
|
|
||||||
externalHandler := panel.Handler(a.ExternalHandler(), cfg.Server.RootDomain)
|
externalHandler := panel.Handler(a.ExternalHandler(), cfg.Server.RootDomain)
|
||||||
internalSrv := &http.Server{Addr: *internalAddr, Handler: a.InternalHandler()}
|
internalSrv := &http.Server{Addr: *internalAddr, Handler: a.InternalHandler()}
|
||||||
externalSrv := &http.Server{Addr: cfg.Server.Listen, Handler: externalHandler}
|
externalSrv := &http.Server{Addr: cfg.Server.Listen, Handler: externalHandler}
|
||||||
|
|||||||
Reference in new issue
Block a user