fix(auth): store passkey backup flags so synced passkeys verify
go-webauthn refuses an assertion whose Backup Eligible flag differs from the stored credential's. The flag was never stored, so it compared against false and every synced passkey (iCloud Keychain, Google Password Manager, 1Password) failed with "Backup Eligible flag inconsistency" - in passwordless sign-in, second-factor sign-in and step-up alike. Registration now stores BackupEligible and BackupState. Rows registered before this have no baseline, so their first verified assertion adopts the signed flag and records it; a recorded value always stands, so a genuine change is still refused.
This commit is contained in:
@@ -47,7 +47,7 @@ func GetPasskeyByCredentialID(instanceID string, credID []byte) (*models.WebAuth
|
||||
return &c, nil
|
||||
}
|
||||
|
||||
func SavePasskey(instanceID, userID, name string, credID, publicKey, aaguid []byte, signCount uint32, transports []string) error {
|
||||
func SavePasskey(instanceID, userID, name string, credID, publicKey, aaguid []byte, signCount uint32, transports []string, backupEligible, backupState bool) error {
|
||||
ctx, cancel := mfaCtx()
|
||||
defer cancel()
|
||||
if name == "" {
|
||||
@@ -62,6 +62,8 @@ func SavePasskey(instanceID, userID, name string, credID, publicKey, aaguid []by
|
||||
AAGUID: aaguid,
|
||||
SignCount: signCount,
|
||||
Transports: transports,
|
||||
BackupEligible: &backupEligible,
|
||||
BackupState: backupState,
|
||||
Name: name,
|
||||
CreatedAt: time.Now(),
|
||||
})
|
||||
@@ -71,13 +73,18 @@ func SavePasskey(instanceID, userID, name string, credID, publicKey, aaguid []by
|
||||
// TouchPasskey records use and the new signature counter. A counter that fails
|
||||
// to advance can mean a cloned authenticator, so the caller checks it before
|
||||
// calling this.
|
||||
func TouchPasskey(instanceID string, credID []byte, signCount uint32) error {
|
||||
func TouchPasskey(instanceID string, credID []byte, signCount uint32, backupEligible, backupState bool) error {
|
||||
ctx, cancel := mfaCtx()
|
||||
defer cancel()
|
||||
now := time.Now()
|
||||
_, err := db.Col("webauthn_credentials").UpdateOne(ctx,
|
||||
bson.M{"instance_id": instanceID, "credential_id": credID},
|
||||
bson.M{"$set": bson.M{"sign_count": signCount, "last_used_at": now}})
|
||||
bson.M{"$set": bson.M{
|
||||
"sign_count": signCount, "last_used_at": now,
|
||||
// Records eligibility on rows that predate it; unchanged otherwise,
|
||||
// because an assertion only succeeds when it matched.
|
||||
"backup_eligible": backupEligible, "backup_state": backupState,
|
||||
}})
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user