fix(passkey): serve flat WebAuthn options to register and username login

go-webauthn marshals CredentialCreation/CredentialAssertion as {"publicKey": {...}},
but the panel's register (Account.tsx) and username-first login (Login.tsx) read the
options flat (options.challenge, options.user.id), so base64urlToBytes(undefined) threw
"Cannot read properties of undefined (reading 'replace')" and neither ceremony could
start. Strip the envelope in the register-begin and username-login-begin handlers via a
small unwrapPublicKey helper; discoverable login keeps the envelope because it reads
options.publicKey.* plus a top-level options.login_id. The begin tests now feed a wrapped
body and assert the handlers return it flat, so they genuinely exercise the unwrap.
This commit is contained in:
flyemoji committed 2026-07-16 13:26:56 +09:00
1 parent be0c4c41f4
commit b5cd4501e5
3 files changed
+30 -11

No files matched your search

+4 -4
View File
@@ -54,14 +54,14 @@ func TestPasskeyRegisterVertical(t *testing.T) {
}
eh := newPasskeyAPI(repo, v, user)
// 1) begin returns the verifier's creation options verbatim and stashes exactly one
// challenge bound to the caller.
// 1) begin returns the creation options FLAT (envelope stripped for the panel) and
// stashes exactly one challenge bound to the caller.
w := do(eh, "POST", "/api/v1/account/passkey/register/begin", `{}`, nil)
if w.Code != http.StatusOK {
t.Fatalf("begin: code = %d, want 200 (%s)", w.Code, w.Body.String())
}
if b := acctBody(t, w); b["publicKey"] == nil {
t.Errorf("begin must return the publicKey creation options verbatim, got %s", w.Body.String())
if b := acctBody(t, w); b["challenge"] == nil || b["publicKey"] != nil {
t.Errorf("begin must return FLAT creation options (top-level challenge, no publicKey envelope), got %s", w.Body.String())
}
if len(repo.passkeyChallenges) != 1 {
t.Fatalf("begin must stash exactly one challenge, got %d", len(repo.passkeyChallenges))