aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Frédéric Guillot <f@miniflux.net> 2024-10-26 18:00:41 -0700
committerGravatar Frédéric Guillot <f@miniflux.net> 2024-10-26 18:36:38 -0700
commit2bcc4b83994a71bee209ad3b24b68492a11610af (patch)
tree48998ad944a73925aad5058005a155e1e3b2c1c9
parentea4d0a4f7208f9ee8b152df3a67dbd4df754c348 (diff)
downloadv2-2bcc4b83994a71bee209ad3b24b68492a11610af.tar.gz
v2-2bcc4b83994a71bee209ad3b24b68492a11610af.tar.zst
v2-2bcc4b83994a71bee209ad3b24b68492a11610af.zip
fix(webauthn): add backup eligibility flag workaround to avoid a 401
Since go-webauthn v0.11.0, the backup eligibility flag is strictly validated, but Miniflux does not store this flag. This workaround to set the flag based on the parsed response, and avoid "BackupEligible flag inconsistency detected during login validation" error. See https://github.com/go-webauthn/webauthn/pull/240
-rw-r--r--internal/ui/webauthn.go56
1 files changed, 46 insertions, 10 deletions
diff --git a/internal/ui/webauthn.go b/internal/ui/webauthn.go
index 921e8f82..710ffd66 100644
--- a/internal/ui/webauthn.go
+++ b/internal/ui/webauthn.go
@@ -206,6 +206,15 @@ func (h *handler) finishLogin(w http.ResponseWriter, r *http.Request) {
json.ServerError(w, r, err)
return
}
+
+ slog.Debug("WebAuthn: parsed response flags",
+ slog.Bool("user_present", parsedResponse.Response.AuthenticatorData.Flags.HasUserPresent()),
+ slog.Bool("user_verified", parsedResponse.Response.AuthenticatorData.Flags.HasUserPresent()),
+ slog.Bool("has_attested_credential_data", parsedResponse.Response.AuthenticatorData.Flags.HasAttestedCredentialData()),
+ slog.Bool("has_backup_eligible", parsedResponse.Response.AuthenticatorData.Flags.HasBackupEligible()),
+ slog.Bool("has_backup_state", parsedResponse.Response.AuthenticatorData.Flags.HasBackupState()),
+ )
+
sessionData := request.WebAuthnSessionData(r)
var user *model.User
@@ -218,34 +227,54 @@ func (h *handler) finishLogin(w http.ResponseWriter, r *http.Request) {
}
}
- var cred *model.WebAuthnCredential
+ var matchingCredential *model.WebAuthnCredential
if user != nil {
- creds, err := h.store.WebAuthnCredentialsByUserID(user.ID)
+ storedCredentials, err := h.store.WebAuthnCredentialsByUserID(user.ID)
if err != nil {
json.ServerError(w, r, err)
return
}
+
sessionData.SessionData.UserID = parsedResponse.Response.UserHandle
- credCredential, err := web.ValidateLogin(WebAuthnUser{user, parsedResponse.Response.UserHandle, creds}, *sessionData.SessionData, parsedResponse)
+ webAuthUser := WebAuthnUser{user, parsedResponse.Response.UserHandle, storedCredentials}
+
+ // Since go-webauthn v0.11.0, the backup eligibility flag is strictly validated, but Miniflux does not store this flag.
+ // This workaround set the flag based on the parsed response, and avoid "BackupEligible flag inconsistency detected during login validation" error.
+ // See https://github.com/go-webauthn/webauthn/pull/240
+ for index := range webAuthUser.Credentials {
+ webAuthUser.Credentials[index].Credential.Flags.BackupEligible = parsedResponse.Response.AuthenticatorData.Flags.HasBackupEligible()
+ }
+
+ for _, webAuthCredential := range webAuthUser.WebAuthnCredentials() {
+ slog.Debug("WebAuthn: stored credential flags",
+ slog.Bool("user_present", webAuthCredential.Flags.UserPresent),
+ slog.Bool("user_verified", webAuthCredential.Flags.UserVerified),
+ slog.Bool("backup_eligible", webAuthCredential.Flags.BackupEligible),
+ slog.Bool("backup_state", webAuthCredential.Flags.BackupState),
+ )
+ }
+
+ credCredential, err := web.ValidateLogin(webAuthUser, *sessionData.SessionData, parsedResponse)
if err != nil {
+ slog.Warn("WebAuthn: ValidateLogin failed", slog.Any("error", err))
json.Unauthorized(w, r)
return
}
- for _, credTest := range creds {
- if bytes.Equal(credCredential.ID, credTest.Credential.ID) {
- cred = &credTest
+ for _, storedCredential := range storedCredentials {
+ if bytes.Equal(credCredential.ID, storedCredential.Credential.ID) {
+ matchingCredential = &storedCredential
}
}
- if cred == nil {
+ if matchingCredential == nil {
json.ServerError(w, r, fmt.Errorf("no matching credential for %v", credCredential))
return
}
} else {
userByHandle := func(rawID, userHandle []byte) (webauthn.User, error) {
var uid int64
- uid, cred, err = h.store.WebAuthnCredentialByHandle(userHandle)
+ uid, matchingCredential, err = h.store.WebAuthnCredentialByHandle(userHandle)
if err != nil {
return nil, err
}
@@ -259,11 +288,18 @@ func (h *handler) finishLogin(w http.ResponseWriter, r *http.Request) {
if user == nil {
return nil, fmt.Errorf("no user found for handle %x", userHandle)
}
- return WebAuthnUser{user, userHandle, []model.WebAuthnCredential{*cred}}, nil
+
+ // Since go-webauthn v0.11.0, the backup eligibility flag is strictly validated, but Miniflux does not store this flag.
+ // This workaround set the flag based on the parsed response, and avoid "BackupEligible flag inconsistency detected during login validation" error.
+ // See https://github.com/go-webauthn/webauthn/pull/240
+ matchingCredential.Credential.Flags.BackupEligible = parsedResponse.Response.AuthenticatorData.Flags.HasBackupEligible()
+
+ return WebAuthnUser{user, userHandle, []model.WebAuthnCredential{*matchingCredential}}, nil
}
_, err = web.ValidateDiscoverableLogin(userByHandle, *sessionData.SessionData, parsedResponse)
if err != nil {
+ slog.Warn("WebAuthn: ValidateDiscoverableLogin failed", slog.Any("error", err))
json.Unauthorized(w, r)
return
}
@@ -275,7 +311,7 @@ func (h *handler) finishLogin(w http.ResponseWriter, r *http.Request) {
return
}
- h.store.WebAuthnSaveLogin(cred.Handle)
+ h.store.WebAuthnSaveLogin(matchingCredential.Handle)
slog.Info("User authenticated successfully with webauthn",
slog.Bool("authentication_successful", true),