Skip to content

feat: add webauthn#895

Merged
muety merged 12 commits intomuety:masterfrom
justin-jiajia:master
Feb 22, 2026
Merged

feat: add webauthn#895
muety merged 12 commits intomuety:masterfrom
justin-jiajia:master

Conversation

@justin-jiajia
Copy link
Contributor

closes #613

Copilot AI review requested due to automatic review settings January 3, 2026 14:04

This comment was marked as spam.

@muety muety self-requested a review January 3, 2026 14:45
@muety
Copy link
Owner

muety commented Jan 3, 2026

No idea how Copilot came up with the idea of reviewing this (should be disabled 🤔), but please ignore those comments. I'll give this a review as soon as possible. Thanks for your contribution.

@justin-jiajia
Copy link
Contributor Author

No idea how Copilot came up with the idea of reviewing this (should be disabled 🤔), but please ignore those comments. I'll give this a review as soon as possible. Thanks for your contribution.

Well I've opened it on my side as I wondered how it worked :)

Don't be that hurry as I'm going to take a exam and maybe work on it again next weekend or later.

BTW: I'm aware that I forgot to use snake-case for attestation json somewhere, and I should brotil the webauthn library too. Will fix them next time I'm free.

@justin-jiajia
Copy link
Contributor Author

BTW: I'm aware that I forgot to use snake-case for attestation json somewhere, and I should brotil the webauthn library too. Will fix them next time I'm free.

Done

Copy link
Owner

@muety muety left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for this PR! Sorry for taking so long with this review and sorry in advance for the number of comments I put. Please have a look at them anyway.

Also, could you please:

  • Add some basic tests for this, if possible? Perhaps do some research how to best tests stuff like this.
  • Add a config option to explicitly enable / disable WebAuthn login, just like you can disable OIDC.

@justin-jiajia
Copy link
Contributor Author

  • Add some basic tests for this, if possible? Perhaps do some research how to best tests stuff like this.

Will do some research next time I'm free. A questions while browsing existing tests: how do you generate the mocks? Are they generated by a program (like mockery )? Or are they written by hand?

  • Add a config option to explicitly enable / disable WebAuthn login, just like you can disable OIDC.

Before implementing it, I want to know: should the users registered through OIDC be allowed to set up passkeys and log in with a passkey? (and why OIDC users should not be able to set a password for themselves, BTW)

@justin-jiajia justin-jiajia requested a review from muety January 24, 2026 14:10
@justin-jiajia
Copy link
Contributor Author

  • Add some basic tests for this, if possible? Perhaps do some research how to best tests stuff like this.

Will do some research next time I'm free. A questions while browsing existing tests: how do you generate the mocks? Are they generated by a program (like mockery )? Or are they written by hand?

Should it be a unit test or an API test? And found a library descope/virtualwebauthn for virtual testing.

  • Add a config option to explicitly enable / disable WebAuthn login, just like you can disable OIDC.

Before implementing it, I want to know: should the users registered through OIDC be allowed to set up passkeys and log in with a passkey? (and why OIDC users should not be able to set a password for themselves, BTW)

Done. There's currently a security.disable_webauthn option. But OIDC users ARE allowed to register passkeys if the option is set to false (the default value). I'm not sure if you think this behavior is expected by you.

@justin-jiajia
Copy link
Contributor Author

@muety Any updates? 👀

@muety
Copy link
Owner

muety commented Feb 6, 2026

Sorry, being super busy rn, will give this a review likely tomorrow!

Copy link
Owner

@muety muety left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for implementing all those change requests!

A questions while browsing existing tests: how do you generate the mocks? Are they generated by a program (like mockery )? Or are they written by hand?

Written by hand.

should the users registered through OIDC be allowed to set up passkeys and log in with a passkey?

No, OIDC users will ever only log in via OIDC.

and why OIDC users should not be able to set a password for themselves, BTW

What would be the point of that? They would never get to enter it anywhere, because authentication is delegated to the OIDC provider and they'll come back logged in.

But OIDC users ARE allowed to register passkeys if the option is set to false (the default value).

When would an OIDC-registered user ever get to use their passkey? They might log in with their OIDC provider (e.g. GitLab) with a passkey they have registered with them, but once a user has OIDC enabled, Wakapi doesn't bother about authenticating the user at all.

Edit: Never mind the above comment. Technically, there's actually nothing against an OIDC user registering a passkey as well. But I'd still like to prevent this to have a clear separation between who is responsible for authentication – either the upstream provider or Wakapi itself. Would you disagree?

Should it be a unit test or an API test? And found a library descope/virtualwebauthn for virtual testing.

Whichever you think is more suitable. In general, there should be tests to check if a user can successfully log in using a passkey (if registered) as well as common error cases (e.g. authenticator signature is invalid / doesn't match the registered credentials). The library you referenced looks promising, feel free to pull it in as a dependency.

Sorry for this review process being so lengthy! 😐

@justin-jiajia
Copy link
Contributor Author

Happened to find a bug related to the hourly breakdown. Fixed via e189a83. I also made a comment about the cause of the bug there.

Before After
image image

@justin-jiajia justin-jiajia requested a review from muety February 12, 2026 15:00
Copy link
Owner

@muety muety left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Last set of change requests, I promise! 🙏 Big thank you for your patience and diligence!

lastCookies []*http.Cookie
)

func TestWebauthn_RegisterAndAuth(t *testing.T) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While you're mostly testing the "happy path", please add some basic failure tests as well, including "Login should fail when an invalid signature is provided" and perhaps a few more "authenticator-related" error cases that come to your mind?

- Removed TotalUsers field from LoginViewModel as it was no longer needed and adjusted tests to remove dependencies on user count.
- Enhanced WebAuthn session management by adding expiration handling.
- Improved WebAuthn tests
- Fixed fetch URL in settings.js for WebAuthn options.
@justin-jiajia
Copy link
Contributor Author

First of all, happy Spring Festival! ❤️ from China.

I've added some tests that I can come up with. But some of the boilerplate test codes and the summary below are AI-generated. Let me know if you think that's not appropriate. I have also removed the TotalUserCount at the login page since it's not used in the html template.

  • TestWebauthn_RegisterDeniedForNonLocalUsers
    Verifies that non-local users cannot register WebAuthn authenticators. Expects a 400 Bad Request and an error message.
  • TestWebauthn_RegisterAndLogin
    Registers a WebAuthn authenticator for a user, then logs in with it. Checks that registration and login both succeed, and the authenticator’s name appears in the settings page.
  • TestWebauthn_RegisterMultipleAndLogin
    Registers two authenticators for the same user. Verifies that both can be used to log in, and each login is successful.
  • TestWebauthn_DeleteAndLoginFailure
    Registers and then deletes an authenticator. Attempts to log in with the deleted authenticator, expecting failure.
  • TestWebauthn_LoginFailureWithWrongAuthenticator
    Registers authenticators for two users. Attempts various logins with mismatched authenticators and users, verifying that only the correct combinations succeed.
  • TestWebauthn_RegisterDuplicateAuthenticatorNameDenied
    Attempts to register two authenticators with the same name for a user. Expects the second registration to fail with a 400 Bad Request and an error message.
  • TestWebauthn_LoginChallengeMismatchReplayProtection
    Simulates a replay attack by submitting an assertion for an old challenge. Expects authentication to fail with a 401 Unauthorized and an error message. ('invalid signature' like you said)
  • TestWebauthn_LoginSuccessThenTimeoutThenFail
    Logs in successfully, then forcibly expires the session, and attempts to log in again with the same assertion. Expects the second login to fail due to session expiry.

@muety muety merged commit 566eae1 into muety:master Feb 22, 2026
2 checks passed
@justin-jiajia
Copy link
Contributor Author

justin-jiajia commented Feb 23, 2026

@muety It seemed that with webauthn disabled on wakapi.dev, user still get the UI to add a passkey. I figured out that there's an extra {{ end }} at /views/settings.tpl.html, line 206 226. Please you remove that line. Really sorry for my carelessness 😢

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

WebAuthn authentication option

3 participants