From cdbb5abc35db5ce6c5433a3afbcfe898d7983c0c Mon Sep 17 00:00:00 2001 From: Minseong Choi Date: Thu, 2 Jul 2026 04:32:34 +0900 Subject: [PATCH] fix(api): record credential id in passkey-register audit event handlePasskeyRegisterFinish logged an empty target for account.passkey.registered, while the delete half logs the credential id. An operator auditing the log could see that a passkey was bound but not which one. Pass cred.ID as the audit target so bind and unbind are symmetric, and tighten the enrollment test to assert both halves name the credential id. --- internal/api/handlers_passkey.go | 2 +- internal/api/handlers_passkey_test.go | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/internal/api/handlers_passkey.go b/internal/api/handlers_passkey.go index fdaf9f8..691928c 100644 --- a/internal/api/handlers_passkey.go +++ b/internal/api/handlers_passkey.go @@ -255,7 +255,7 @@ func (a *API) handlePasskeyRegisterFinish(w http.ResponseWriter, r *http.Request writeError(w, r, err) return } - a.audit(r, auditActor(p), "account.passkey.registered", "") + a.audit(r, auditActor(p), "account.passkey.registered", cred.ID) writeJSON(w, http.StatusCreated, passkeyView(cred)) } diff --git a/internal/api/handlers_passkey_test.go b/internal/api/handlers_passkey_test.go index 35a95c2..764f543 100644 --- a/internal/api/handlers_passkey_test.go +++ b/internal/api/handlers_passkey_test.go @@ -125,18 +125,20 @@ func TestPasskeyRegisterVertical(t *testing.T) { t.Fatalf("delete left %d credentials, want 0", len(repo.passkeyCreds)) } - // Both mutating halves audit by the caller's Access email. + // Both mutating halves audit by the caller's Access email AND name the affected + // credential id as the target, so an operator reading the log can tell which + // passkey was bound/unbound (register previously logged an empty target). var registered, removed bool for _, a := range repo.audits { switch a.Action { case "account.passkey.registered": - registered = a.Actor == "u1@example.net" + registered = a.Actor == "u1@example.net" && a.ServerName == id case "account.passkey.removed": - removed = a.Actor == "u1@example.net" + removed = a.Actor == "u1@example.net" && a.ServerName == id } } if !registered || !removed { - t.Errorf("want registered+removed audits by u1@example.net, got %+v", repo.audits) + t.Errorf("want registered+removed audits by u1@example.net targeting %s, got %+v", id, repo.audits) } }