Skip to content

feat: add passkey support#6375

Open
DeDiamondPro wants to merge 3 commits into
modrinth:boris/dev-908-backend-changesfrom
DeDiamondPro:feat/passkeys
Open

feat: add passkey support#6375
DeDiamondPro wants to merge 3 commits into
modrinth:boris/dev-908-backend-changesfrom
DeDiamondPro:feat/passkeys

Conversation

@DeDiamondPro

@DeDiamondPro DeDiamondPro commented Jun 12, 2026

Copy link
Copy Markdown

This pull request adds support for logging in with passkeys, and managing passkeys.

I based this PR on #5790 since that is the PR IMB11 told me to look at, and to reduce unnecessary merge conflicts later.

Labrinth

The backend is implemented using the webauthn-rs library. The new routes are added in the routes/internal/flow.rs file since the other authentication routes also seemed to be implemented there. I tried to be as consistent as possible with the other code, if anything needs updating please do tell me.

New routes:

Register:
POST /auth/passkey/register/start: generates options and challenge
POST /auth/passkey/register/finish: verifies and stores new credential
Login:
POST /auth/passkey/start: generates options and challenge
POST /auth/passkey/finish: looks up and verifies attestation for credential
Manage:
GET /auth/passkey: lists passkeys
PATCH /auth/passkey/[id]: rename passkey
DELETE /auth/passkey/[id]: delete passkey

Website

The sign in button was implemented by re-using the styles of the oauth cards, I hope this is okay, but this made sense to me since the button is styled exactly the same.
For the settings page I made a separate component since quite a few modals are needed, and the account page was growing quite a bit when I had it there, if this should be moved somewhere else or back to the account page please do tell me. For the manage modal I tried to match the style of the PAT page, since almost the same properties are displayed for both.

Flow

The flow for registering a passkey is:

  1. Labrinth provides enrollment options to client, resident key is set to required since currently only usernameless login with passkeys is implemented, so you need it to be a resident credential to allow it to work with discoverable credentials.
  2. Client calls platform API to start enrollment process, returns an attestation
  3. The user chooses a name for the credential
  4. Name and attestation are sent to backend, where the attestation is verified and stored

The flow for authentication with a passkey is:

  1. Labrinth provides options and a challenge for a discoverable credential flow, these options require user verification (biometric, pin input, ...) to be performed
  2. Client calls platform API to start authentication process, user can select credential if they have multiple, returns attestation
  3. Labrinth looks up the credential, and verifies the attestation, returns a session

What this does not do

This does not implement a 2FA flow for passkeys/security keys. This is for a couple of reasons:

  1. I wanted to keep this pr as small as I could (it's already a lot bigger than I anticipated)
  2. Quite a few design decisions need to be made to implement this well, and I did not feel comfortable making these decisions on my own. For example some things that need to be considered are:
    • What is the default shown? TOTP or security key? And if we used what the user last used is this stored server side or client side?
    • Does adding a passkey automatically enable 2FA? It probably shouldn't since if you lose your hardware passkey for example you will be locked out of your account. But then how is this handled in UX so it is clear to the user? And if you have 2+ pass/security keys, should it now enable 2FA even if no TOTP is registered?
  3. I believe the username- and password-less login flow implemented here is the most used usecase for passkeys.

If you want I can implement this but then I'd like some more info about what the preferred way to implement it would be.

Small demo

2026-06-12.11-18-10.mp4

@modrinth-bot

modrinth-bot commented Jun 12, 2026

Copy link
Copy Markdown
Member

Note

This changelog has been merged into the changelog for #5790

Pull request changelog

App

Added

Changed

Deprecated

Removed

Fixed

Security

Website

Added

Changed

Deprecated

Removed

Fixed

Security

Hosting

Added

Changed

Deprecated

Removed

Fixed

Security

@DeDiamondPro

DeDiamondPro commented Jun 12, 2026

Copy link
Copy Markdown
Author

I tried to fix the merge conflicts but I'm getting hydration failures (or at least I think that's what is going on), I'll try to fix it and push the merge either tonight or tomorrow.

@IMB11 IMB11 requested review from a team and aecsocket June 12, 2026 15:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants