Repository navigation
feat(auth): passkeys as a second factor (#106) - #110
Merged
Merged
Conversation
An account can now hold an authenticator app, passkeys, or both. Two-factor is on while at least one exists: the first factor added issues the recovery codes and ends earlier sessions, removing the last one turns it off. The WebAuthn ceremony runs in the browser, on the portal. A client opens it with a one-time handle and gets a single-use code back, by a loopback redirect on desktop, a voltius:// link on a phone, or in the response for the portal's own sign-in. A sign-in code only fits the pending token it was opened for, so pending tokens now carry a unique id: two issued in the same second used to be identical. Step-up actions accept a passkey code beside a TOTP or recovery code, and adding a factor to an account that has one needs proof of it. A TOTP seed that was never confirmed no longer passes as a factor at sign-in. The relying party comes from WEBAUTHN_RP_ID and VOLTIUS_APP_URL; without it the server offers no passkeys and /v1/meta says so. webauthn-rs links OpenSSL statically, hence openssl-libs-static in the builder image.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #106. Server side of passkeys as a second factor. Merge and deploy this one first; the portal and client PRs work against an older server and simply offer no passkeys.
What changes
127.0.0.1:<port>for the desktop app, avoltius://passkeylink for a phone, or in the response for the portal's own sign-in.passkey_codeis accepted wherevercodeandrecovery_codeare:login/2fa, disable, recovery codes, password change, and adding another factor.WEBAUTHN_RP_ID. Unset, no passkeys and/v1/metasayspasskeys: false. The portal origin isVOLTIUS_APP_URL.user_passkeys,passkey_ceremonies. Ceremony state lives in Postgres, nothing per process.Endpoints
POST /v1/auth/login/2fa/passkeyPOST /v1/auth/passkey/begin,/finishAUTH_RATE_LIMIT)GET /v1/auth/2faPOST /v1/auth/2fa/passkeys/register,/activatePOST /v1/auth/2fa/passkeys/step-upPOST /v1/auth/2fa/passkeys/:id/remove,/v1/auth/2fa/totp/remove/2fa/setupnow takes proof when the account already has a factor./2fa/disableremoves everything and also returnstwo_factor_enabled. The admin clear removes passkeys too.Things a reviewer should look at
jti. Two sign-ins for one account in the same second produced identical pending tokens, so a passkey code bound to one fitted the other. A test caught it by flaking.passkey_codedoes count.openssl-libs-staticin the builder image. webauthn-rs links OpenSSL and the musl build is static. The release image builds and runs; checked on arm64.Deploy
Set
WEBAUTHN_RP_IDbefore the first passkey is registered and never change it: every passkey is bound to it. For the hosted servicevoltius.appcoversapp.voltius.appand survives moving the portal. It is not a secret.Tests
730 pass (13 new), clippy clean. The passkey tests drive real registration and assertion with webauthn-rs's software authenticator. Also run end to end in Chromium with a virtual authenticator against the portal branch, and from the desktop app.