What changed, and why it matters
This commit changes the WebAuthn/FIDO login JavaScript so that it now sends an extra piece of information, called the userHandle, to the server when a user logs in with a security key or passkey. The userHandle helps the server identify which account the credential belongs to. Without it, the server may have to guess the account from the credential ID alone, which can cause login failures or, in some FIDO implementations, allow a credential to be associated with the wrong account. The change is small and looks like a correctness fix rather than a full security patch.
Review the server-side WebAuthn assertion verification code to confirm it now consumes userHandle and validates it against the expected account. If the server ignores the field, deploy a corresponding server-side update. Test login flows with both resident/discoverable credentials and non-resident credentials, and consider adding regression tests for multi-account credential scenarios.
Security signals we found
Adds userHandle to WebAuthn assertion response sent to server
Client-side-only change; server-side verification behavior is not visible
Aligns with FIDO2/WebAuthn spec guidance for resident-key / discoverable-credential flows
Could mitigate user-account confusion when credential ID alone is ambiguous
No explicit security framing, CVE, or advisory in commit metadata
Evidence from the diff
In BTCPayServer/wwwroot/js/webauthn/login.js, verifyAssertionWithServer now extracts assertedCredential.response.userHandle, converts it to a Uint8Array (or null if absent), and includes it in the response payload sent to the server. The userHandle is a UTF-8 byte sequence returned by the authenticator during assertion, defined by WebAuthn as the user.id that was supplied at registration. Passing it to the server is recommended by the FIDO/WebAuthn spec so the Relying Party can identify the user account when allowCredentials contains credentials usable by multiple accounts. The patch is a client-side-only change; whether it fixes a security issue depends on how the server-side assertion verification consumes this field, which is not shown in the diff.
Changed components
BTCPayServer/wwwroot/js/webauthn/login.jsWebAuthn/FIDO2 login flowClient-side assertion response serializationInspect captured patch +3 / −1
diff --git a/BTCPayServer/wwwroot/js/webauthn/login.js b/BTCPayServer/wwwroot/js/webauthn/login.js
index eb73259..7e46ff6 100644
--- a/BTCPayServer/wwwroot/js/webauthn/login.js
+++ b/BTCPayServer/wwwroot/js/webauthn/login.js
@@ -33,6 +33,7 @@ async function verifyAssertionWithServer(assertedCredential) {
let clientDataJSON = new Uint8Array(assertedCredential.response.clientDataJSON);
let rawId = new Uint8Array(assertedCredential.rawId);
let sig = new Uint8Array(assertedCredential.response.signature);
+ let userHandle = assertedCredential.response.userHandle ? new Uint8Array(assertedCredential.response.userHandle) : null;
const data = {
id: assertedCredential.id,
rawId: coerceToBase64Url(rawId),
@@ -41,7 +42,8 @@ async function verifyAssertionWithServer(assertedCredential) {
response: {
authenticatorData: coerceToBase64Url(authData),
clientDataJSON: coerceToBase64Url(clientDataJSON),
- signature: coerceToBase64Url(sig)
+ signature: coerceToBase64Url(sig),
+ userHandle: userHandle ? coerceToBase64Url(userHandle): null
}
};
Why this scored 33/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.