Implement a dialog for setting new device PIN - #135
Conversation
577d103 to
7bef362
Compare
Yeah, this is getting pretty clunky to add new features into. I think though that we're treating PinNotSet as a terminal condition, but it feels more like a branch of user interaction. (In typing this up, I've already lost the thread... I need some sort of diagram for this.) But maybe we can play around with
This doesn't give us everything we need (for example, we probably want the UI to be aware that we're redoing the request after the PIN... maybe the UI can just keep that state on their end?), and might not even work. But maybe that's worth playing with. We may need to shuffle some things around. E.g., instead of storing then spawning a new handle_events task after re-entering the Connected state might result in some weirdness. Maybe that needs to be moved up outside of the state machine loop. |
|
Making it a user interaction flow is a very good idea. We might even implement this in libwebauthn directly and expose it the same way we expose |
…onform to the PIN policy
…ng, because of cancel-request intervening)
7bef362 to
44e1c23
Compare
|
Ok, I was finally able to finish this, using the not yet merged PR from linux-credentials/libwebauthn#181 I think this is now a bit cleaner, although I think we can simplify the UI flow a bit more. Keeping it a draft until we can switch to the official libwebauthn. I haven't touched the API.md doc yet, because I wanted to wait if this is fine as is. |
Add interactive possibility to 'fix' `PINNotSet` errors, same as `PINRequired`, to help with linux-credentials/credentialsd#135 - For testing, the `webauthn_{hid,nfc}` examples now have a question whether it should use UV preferred or required, to be able to test both (`PINNotSet` obviously only is returned if UV is required) - Instead of erroring out early as we did before, the new algorithm (hopefully) allows for devices that can handle this internally to continue. Sadly, I don't have such a device to test this. - Split the `PinManagement`-trait into and internal and public API, which is a bit hacky. - Reused `change_pin()` from `PinManagement`, which can both change or set a PIN. Not super pretty, but should do in this case. This PR should probably stay open until I managed to fully integrated it into the credentialsd PR. --------- Co-authored-by: Alfie Fresta <afresta@noentropy.org>
|
@msirringhaus this should work with libwebauthn-v0.3.0 on crates.io. (I would update this myself but the PR is on your personal fork so I don't have permissions.) |
There was a problem hiding this comment.
Pull request overview
Adds an interactive device-PIN setup flow for WebAuthn registration over USB and NFC.
Changes:
- Adds PIN setup events and D-Bus signaling.
- Implements authenticator PIN handling and policy errors.
- Adds GTK PIN entry and confirmation screens.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
credentialsd/src/dbus/ui_control.rs |
Relays PIN setup events. |
credentialsd/src/dbus/flow_control.rs |
Routes new PINs to authenticators. |
credentialsd/src/credential_service/usb.rs |
Implements USB PIN setup. |
credentialsd/src/credential_service/nfc.rs |
Implements NFC PIN setup handling. |
credentialsd-ui/src/gui/view_model/mod.rs |
Processes PIN submission events. |
credentialsd-ui/src/gui/view_model/gtk/window.rs |
Adds PIN dialog callbacks. |
credentialsd-ui/src/gui/view_model/gtk/mod.rs |
Exposes PIN-related UI state. |
credentialsd-ui/src/gui/mod.rs |
Adds the PIN-not-set update. |
credentialsd-ui/src/dbus.rs |
Adds PIN setup D-Bus methods. |
credentialsd-ui/src/client.rs |
Sends PINs through protected descriptors. |
credentialsd-ui/po/de_DE.po |
Adjusts translation catalog formatting. |
credentialsd-ui/data/resources/ui/window.blp |
Defines the PIN setup screens. |
credentialsd-common/src/server.rs |
Adds currently unexported legacy serialization code. |
credentialsd-common/src/model.rs |
Defines shared PIN events and errors. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| None => tracing::debug!("Pin channel closed before receiving pin from client."), | ||
| } | ||
| } | ||
| UvUpdate::PinNotSet(pin_update) => { |
There was a problem hiding this comment.
Yes, NFC needs to be fixed first in the general code, before we can use it with this.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 13 out of 13 changed files in this pull request and generated no new comments.
Suppressed comments (2)
credentialsd/src/credential_service/usb.rs:288
- This comment says the reason is checked, but the match deliberately forwards every repeated
PinNotSetupdate, including an unchanged reason, because each update has a fresh sender. Please describe that requirement directly so a future cleanup does not reintroduce the stalled-retry bug.
// Usually, comparing the Discrimimant is enough, but for PinNotSet, we have to check if the reason changed
// as this state can be repeated multiple times with different or the same error
// reasons (PIN too short, PIN too long, etc.)
credentialsd-ui/data/resources/ui/window.blp:267
- The newly marked UI messages are absent from
po/credentialsd-ui.potand every locale catalog, whereas the existing translatable messages are represented there (for example,Completeatpo/credentialsd-ui.pot:74). As shipped, this entire dialog and its error text therefore fall back to English for localized users. Regenerate the POT file and merge the new entries into the PO files.
title: _("Set a PIN");
Only NFC and USB for now. Hybrid makes no sense, and I don't have anything to test BLE right now. USB and NFC both work.
Flow of how this works:
Upon "PinNotSet"-error during registration, there will be a page telling the user that the RP requires additional protection on the device, e.g. a PIN, which will affect the whole device. There are two buttons: "Close", "Set PIN on device". Close closes the window. "Set PIN on device" brings the user to another page that offers 2 password-entry fields. "Continue" will be disabled until both fields are non-empty and identical. On pressing "Continue", the PIN is set for the device.
Some known issues with this implementation:
On error (e.g. PinTooShort) the window just swaps to the general error page and the user needs to close and start again.[solved. User is now dropped to the page with the "Set PIN on device"-button, with error text about PIN policy violation]On success, the window simply closes. The PIN is now set and the request needs to be issued again. But this is not really communicated in a good way.This is actually a general bug. The window is supposed to stay open for a bit in general to show "Done", but in practice gets closed right away because some cancel-request overrides it.No check yet if the device even supports a PIN, before we show this dialog. We would need the deviceInfo on the UI-side for this, which we currently don't have. Or an additional API call to askFor this specific case, it's ok, becausecredentialsdwhat kind of UV the device supports?CTAP2_ERR_PIN_NOT_SETis only ever returned by the device if it supports a PIN. But e.g. biometrics enrollment wouldn't work without having the device-info everywhere.credentialsdhandles the event, but also couldn't come up with a better approach. It basically starts a fresh "Select the device you want, then continue with request 'Setting new PIN' on that device"-cycle, which may result in weird UX, if multiple devices are plugged in. But I also don't really know how to go directly for the previously selected device.Thus, I marked this PR a draft for now. Maybe @iinuwa has some suggestions for some of these problems?