🐛 Bug Report: Shared passkey between KeePassXC and Keepassium does not work #269

Closed
opened 2025-10-07 00:08:03 +03:00 by OVERLORD · 11 comments
Owner

Originally created by @Bastian on GitHub.

Reproduction steps

  • Add a passkey using KeePassXC on the desktop
  • Try to login with the stored passkey from a mobile iOS device using Keepassium
  • You will get an error in the UI and server logs

Expected behavior

Sharing a passkey between desktop and mobile should work

Actual Behavior

When I add a passkey on my desktop with KeePassXC (Linux with the Firefox extension) and then try to use the stored passkey on my mobile device with Keepassium (iOS), I cannot authenticate and get the error message "Something went wrong" in the web UI. The error "BackupEligible flag inconsistency detected during login validation" is logged in the server logs.

The same thing happens in reverse: if I create the passkey on my mobile device and then try to use it on the desktop, the same error occurs.

When I use the passkey on the device it was created on, everything works as expected.

I'm not entirely sure which application is misbehaving here, especially since passkey support in Keepassium is still fairly new. However, on other websites (e.g. Amazon), sharing a passkey between Keepassium and KeepassXC works without any problems.

I have also tried the Pocket ID Demo and have the same problem here. So I can at least rule out that it is a problem with my Pocket ID deployment.

Version and Environment

Pocket-ID: v0.44.0
KeePassXC 2.7.0
Keepassium v2.3.163 Pro

Log Output

[GIN] 2025/03/27 - 19:15:44 | 401 |     459.319µs |             ::1 | GET      "/api/users/me"
Error #01: You are not signed in
[GIN] 2025/03/27 - 19:15:44 | 401 |     555.612µs |             ::1 | GET      "/api/users/me"
Error #01: You are not signed in
[GIN] 2025/03/27 - 19:15:44 | 200 |    1.205841ms |             ::1 | GET      "/api/application-configuration"
[GIN] 2025/03/27 - 19:15:44 | 200 |    1.263245ms |             ::1 | GET      "/api/application-configuration"
[GIN] 2025/03/27 - 19:15:46 | 200 |   16.168712ms | 192.168.178.203 | GET      "/api/webauthn/login/start"
[GIN] 2025/03/27 - 19:15:46 | 200 |   16.225119ms | 192.168.178.203 | GET      "/api/webauthn/login/start"
[GIN] 2025/03/27 - 19:15:50 | 500 |    4.928095ms | 192.168.178.203 | POST     "/api/webauthn/login/finish"
Error #01: BackupEligible flag inconsistency detected during login validation
[GIN] 2025/03/27 - 19:15:50 | 500 |    4.996698ms | 192.168.178.203 | POST     "/api/webauthn/login/finish"
Error #01: BackupEligible flag inconsistency detected during login validation

Image

Originally created by @Bastian on GitHub. ### Reproduction steps - Add a passkey using KeePassXC on the desktop - Try to login with the stored passkey from a mobile iOS device using Keepassium - You will get an error in the UI and server logs ### Expected behavior Sharing a passkey between desktop and mobile should work ### Actual Behavior When I add a passkey on my desktop with [KeePassXC](https://keepassxc.org/) (Linux with the Firefox extension) and then try to use the stored passkey on my mobile device with [Keepassium](https://github.com/keepassium/KeePassium) (iOS), I cannot authenticate and get the error message "Something went wrong" in the web UI. The error "BackupEligible flag inconsistency detected during login validation" is logged in the server logs. The same thing happens in reverse: if I create the passkey on my mobile device and then try to use it on the desktop, the same error occurs. When I use the passkey on the device it was created on, everything works as expected. I'm not entirely sure which application is misbehaving here, especially since passkey support in Keepassium is still fairly new. However, on other websites (e.g. Amazon), sharing a passkey between Keepassium and KeepassXC works without any problems. I have also tried the [Pocket ID Demo](https://demo.pocket-id.org/start-demo) and have the same problem here. So I can at least rule out that it is a problem with my Pocket ID deployment. ### Version and Environment Pocket-ID: v0.44.0 KeePassXC 2.7.0 Keepassium v2.3.163 Pro ### Log Output ``` [GIN] 2025/03/27 - 19:15:44 | 401 | 459.319µs | ::1 | GET "/api/users/me" Error #01: You are not signed in [GIN] 2025/03/27 - 19:15:44 | 401 | 555.612µs | ::1 | GET "/api/users/me" Error #01: You are not signed in [GIN] 2025/03/27 - 19:15:44 | 200 | 1.205841ms | ::1 | GET "/api/application-configuration" [GIN] 2025/03/27 - 19:15:44 | 200 | 1.263245ms | ::1 | GET "/api/application-configuration" [GIN] 2025/03/27 - 19:15:46 | 200 | 16.168712ms | 192.168.178.203 | GET "/api/webauthn/login/start" [GIN] 2025/03/27 - 19:15:46 | 200 | 16.225119ms | 192.168.178.203 | GET "/api/webauthn/login/start" [GIN] 2025/03/27 - 19:15:50 | 500 | 4.928095ms | 192.168.178.203 | POST "/api/webauthn/login/finish" Error #01: BackupEligible flag inconsistency detected during login validation [GIN] 2025/03/27 - 19:15:50 | 500 | 4.996698ms | 192.168.178.203 | POST "/api/webauthn/login/finish" Error #01: BackupEligible flag inconsistency detected during login validation ``` ![Image](https://github.com/user-attachments/assets/d8a5dcae-b1f4-4a83-bf5d-3ccaaffd36e5)
OVERLORD added the bug label 2025-10-07 00:08:03 +03:00
Author
Owner

@Bastian commented on GitHub:

Keepassium would be the misbehaving app in the scenario described.

The same happens when creating the passkey in Keepassium and trying to use it in KeepassXC. I might be wrong, but the information about what flags were used during creation is not stored in the Keepass database, is it? So either application is currently not able to behave correctly when sharing passkeys.

@Bastian commented on GitHub: > Keepassium would be the misbehaving app in the scenario described. The same happens when creating the passkey in Keepassium and trying to use it in KeepassXC. I might be wrong, but the information about what flags were used during creation is not stored in the Keepass database, is it? So either application is currently not able to behave correctly when sharing passkeys.
Author
Owner

@stonith404 commented on GitHub:

I'm 99% sure that this is a bug of KeePassXC.

It seems like that in the code of Keepassium the "BackupEligible" gets set to true and in the code of KeePassXC gets set to false. I have no experiencing in creating Webauthn credentials though, I might also be wrong.

Since this issue has not been reported with any other passkey provider, I am closing it because it is very likely not related to Pocket ID.

@stonith404 commented on GitHub: I'm 99% sure that this is a bug of KeePassXC. It seems like that in the [code of Keepassium](https://github.com/keepassium/KeePassium/blob/master/KeePassiumLib/KeePassiumLib/db/passkey/Passkey.swift#L172C21-L172C34) the "BackupEligible" gets set to `true` and in the [code of KeePassXC ](https://github.com/keepassxreboot/keepassxc/blob/af2479da8dc0ff0c7104c4ccde1715b1c562dfdc/src/browser/BrowserPasskeys.cpp#L212) gets set to `false`. I have no experiencing in creating Webauthn credentials though, I might also be wrong. Since this issue has not been reported with any other passkey provider, I am closing it because it is very likely not related to Pocket ID.
Author
Owner

@kmendell commented on GitHub:

This is interesting, I share my passkeys in Bitwarden and i have no issues, im wondering if this is a KeePassXC issue?

@kmendell commented on GitHub: This is interesting, I share my passkeys in Bitwarden and i have no issues, im wondering if this is a KeePassXC issue?
Author
Owner

@droidmonkey commented on GitHub:

Since the key was created in keepassxc then we technically get say over backup eligibility per the spec. Keepassium would be the misbehaving app in the scenario described.

However, the real problem is that we are both using hardcoded values for backup eligibility and implemented those values differently.

I read the spec on this, is it within spec to reject authentication because of a BE mismatch? Despite its strong wording on not changing the value, it is a non-cryptographically, authenticator reported setting that really means nothing for the authentication ceremony.

https://www.w3.org/TR/webauthn-3/#sctn-credential-backup

The spec gives example of what to do with this flag and that does not include rejection on stored mismatch.

@droidmonkey commented on GitHub: Since the key was created in keepassxc then we technically get say over backup eligibility per the spec. Keepassium would be the misbehaving app in the scenario described. However, the real problem is that we are both using hardcoded values for backup eligibility and implemented those values differently. I read the spec on this, is it within spec to reject authentication because of a BE mismatch? Despite its strong wording on not changing the value, it is a non-cryptographically, authenticator reported setting that really means nothing for the authentication ceremony. https://www.w3.org/TR/webauthn-3/#sctn-credential-backup The spec gives example of what to do with this flag and that does not include rejection on stored mismatch.
Author
Owner

@Bastian commented on GitHub:

Thank you for investigating the problem and pointing me in the right direction! I'll experiment with some other clients, and if I can pinpoint the problem back to KeePassXC, I'll open an issue there.

@Bastian commented on GitHub: Thank you for investigating the problem and pointing me in the right direction! I'll experiment with some other clients, and if I can pinpoint the problem back to KeePassXC, I'll open an issue there.
Author
Owner

@stonith404 commented on GitHub:

@droidmonkey Thanks for your comment. To be honest I'm not an expert in Webauthn and I haven't read the spec. We are using go-webauthn for passkey authentication and this library returns an error if the backup eligibility mismatches.

The author has mentioned that the spec defines that credentials should get rejected if the flag mismatches. I skimmed the spec and I didn't find that credentials should get rejected if the flag mismatched but the author of this library is probably an expert in Webauthn so I don't want to contradict him. If you think that rejecting the credential is wrong, I could create an issue in go-webauthn.

@stonith404 commented on GitHub: @droidmonkey Thanks for your comment. To be honest I'm not an expert in Webauthn and I haven't read the spec. We are using [go-webauthn](https://github.com/go-webauthn/webauthn) for passkey authentication and this library [returns an error if the backup eligibility mismatches](https://github.com/go-webauthn/webauthn/blob/fb973e9bef6a699a34a9f3efc32ce026ef8fac4e/webauthn/login.go#L377). The author [has mentioned](https://github.com/go-webauthn/webauthn/issues/335) that the spec defines that credentials should get rejected if the flag mismatches. I skimmed the spec and I didn't find that credentials should get rejected if the flag mismatched but the author of this library is probably an expert in Webauthn so I don't want to contradict him. If you think that rejecting the credential is wrong, I could create an issue in [go-webauthn](https://github.com/go-webauthn/webauthn).
Author
Owner

@droidmonkey commented on GitHub:

That language still does not say to reject authentication. It supports my opinion of an option within relying party implementations to decide whether or not to issue a "warning" or to be more strict and reject the authentication. If go-webauthn does not give this option, then I think that is a missing feature.

@droidmonkey commented on GitHub: That language still does not say to reject authentication. It supports my opinion of an option within relying party implementations to decide whether or not to issue a "warning" or to be more strict and reject the authentication. If go-webauthn does not give this option, then I think that is a missing feature.
Author
Owner

@droidmonkey commented on GitHub:

I personally don't think it is appropriate to reject authentication since that is not explicitly required in the spec. At least not in the sections regarding backup enablement.

I do think a warning should be issued, and a toggle flag for "strictness" be available to better control the desired outcome/behavior.

@droidmonkey commented on GitHub: I personally don't think it is appropriate to reject authentication since that is not explicitly required in the spec. At least not in the sections regarding backup enablement. I do think a warning should be issued, and a toggle flag for "strictness" be available to better control the desired outcome/behavior.
Author
Owner

@droidmonkey commented on GitHub:

That is correct the information is not stored or read even if it was stored. Not saying we don't need to fix something 😁

@droidmonkey commented on GitHub: That is correct the information is not stored or read even if it was stored. Not saying we don't need to fix something 😁
Author
Owner

@Bastian commented on GitHub:

I have found the relevant part of the spec that clearly states that the BE flag should be verified:

Image

(7.2. Verifying an Authentication Assertion)

It was introduced in PR https://github.com/w3c/webauthn/pull/1907 as a response to issue https://github.com/w3c/webauthn/issues/1791.

@Bastian commented on GitHub: I have found the relevant part of the spec that clearly states that the BE flag should be verified: <img width="790" alt="Image" src="https://github.com/user-attachments/assets/feb0c5ce-3c42-4299-bbb4-23f2b2688fc7" /> _([7.2. Verifying an Authentication Assertion](https://www.w3.org/TR/webauthn-3/#sctn-verifying-assertion))_ It was introduced in PR https://github.com/w3c/webauthn/pull/1907 as a response to issue https://github.com/w3c/webauthn/issues/1791.
Author
Owner

@Bastian commented on GitHub:

The only exception when verification is not required is if the backup state is not part of the RP’s business logic or policy.

Looking at the discussion in the PR, the intention for this exception seems to be that the RPs are not required to store the initial value, and thus might not be able to enforce it.

See https://github.com/w3c/webauthn/pull/1907#discussion_r1233918349:

the comparison with |credentialRecord|.[$credential record/backupEligible$] probably needs to stay within the qualified case since it's optional for the RP to store that value.

@Bastian commented on GitHub: The only exception when verification is not required is if the backup state is not part of the RP’s business logic or policy. Looking at the discussion in the PR, the intention for this exception seems to be that the RPs are not required to store the initial value, and thus might not be able to enforce it. See https://github.com/w3c/webauthn/pull/1907#discussion_r1233918349: > the comparison with |credentialRecord|.[$credential record/backupEligible$] probably needs to stay within the qualified case since it's optional for the RP to store that value.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/pocket-id#269