cms icon indicating copy to clipboard operation
cms copied to clipboard

[5.x] Add passkey 🔑 support to CP logins

Open ryanmitchell opened this issue 7 months ago • 19 comments

This PR adds WebAuthn/Passkey support to the CP login:

https://github.com/statamic/cms/assets/51899/97eca681-2d55-478c-8ed8-0dfd88aa796d

We make use of the following libraries for backend and front end support respectively: https://github.com/web-auth/webauthn-lib https://simplewebauthn.dev/docs/packages/browser/

I noticed CraftCMS have used the same libraries for their Passkey support.

The user flow is as follows:

  • An already authenticated user goes to the Passkeys page (new option under user menu) where they can create or remove Passkeys.
  • Once a passkey has been created (through the Browsers in-built UI) the user then has the option to login using that on the CP login page. It should 'just work'.

The changes behind the scenes are:

  • There is a new webauthn config file, allow this functionality to be enabled ~~- There is a new passkeys stache store (inside a new passkeys folder in the users folder)~~ ~~- There is a passkeys repository and query builder to allow querying of a user's passkeys~~

~~I still need to add support for eloquent stored passkeys, and~~ the UI on the passkeys page in the CP could do with being improved by someone who has better design skills than me.

Closes https://github.com/statamic/ideas/issues/1059

ryanmitchell avatar Dec 22 '23 08:12 ryanmitchell

This is amazing. I just did a test run and with SSL enabled locally and "web-auth/webauthn-lib": "^4.7" added to my composer.json I was able to create a passkey and login. Very hot stuff!

A side note regarding the login flow - and this should probably be addressed for oAuth as well in a separate PR - I'd ditch the Login with email button:

Screenshot_2023-12-22_at_12 22 09

And instead always show the regular login fields and below that options for other types of login: passkeys, oauth. This is basically the flow GitHub uses:

https://github.com/statamic/cms/assets/69107412/bfec98c0-cc70-4377-871b-ae6b7c64f439

robdekort avatar Dec 22 '23 11:12 robdekort

One thing I noticed is that the users table lists the key as a user. Screenshot 2023-12-22 at 12 35 44

robdekort avatar Dec 22 '23 11:12 robdekort

jasonvarga avatar Jan 04 '24 16:01 jasonvarga

One note regarding this that I don't remember being in this PR. I feel Statamic should disable the option to login with password for the account that has a Passkey set. That seems to be the standard, as otherwise there's no security benefit, but it's just a convenience.

robdekort avatar Apr 18 '24 18:04 robdekort

Looking at GitHub as an example, I have set up a passkey there, but they still give me the option to sign in with a username and password. Both work.

jasonvarga avatar Apr 18 '24 18:04 jasonvarga

Looking at GitHub as an example, I have set up a passkey there, but they still give me the option to sign in with a username and password. Both work.

I see, you’re right regarding GitHub. I’m honestly surprised by that. That is not what I see most sites that offer Passkeys do. For example my accounting software completely disables password logins as soon as you set a Passkey. And so does Sony on their recently introduced Passkeys for PS5.

I believe this should be how it’s done. Passkeys are here to eliminate passwords and improve security. By keeping the ability to login with a password there’s no security benefit gained.

robdekort avatar Apr 18 '24 18:04 robdekort

Do you mean the option is gone from the form? Or you just get denied if you try to enter a username/password?

jasonvarga avatar Apr 18 '24 18:04 jasonvarga

I have a PS account so I could test that myself. It seems that you have to enter your username first, then it gives you the appropriate option. e.g. I entered my email then it showed me the option to sign in with a passkey. I tried again with a fake email and it shows me the password field.

jasonvarga avatar Apr 18 '24 18:04 jasonvarga

Do you mean the option is gone from the form? Or you just get denied if you try to enter a username/password?

Yeah, getting denied. Sony disabled my password and 2fa, no way to enable it with a Passkey enabled. Here you can see it. Sorry Dutch, but I think you'll understand: Screenshot 2024-04-18 at 21 02 18

This is my accounting software. Also Dutch, but when I try to login with my e-mail it says nope: Screenshot 2024-04-18 at 21 03 54

robdekort avatar Apr 18 '24 19:04 robdekort

Weirdly enough Apple does offer the password option. Perhaps because I had 2fa enabled, I'm not sure. Screenshot 2024-04-18 at 21 05 56

I guess it's a choice. But security wise I don't think it makes sense to keep the option.

robdekort avatar Apr 18 '24 19:04 robdekort

Nice I love inconsistency. So I think maybe we do this:

Have an allow_password_login_with_passkey config that controls whether you can login with a password if you have a passkey set.

I assume the difference across the sites is because of different requirements on a per-company scope. Some might like it one way, others not.

This options should allow most situations:

  • Users without a passkey can still log in with email/password.
  • Users with a passkey that enter their password can still log in, if you configure it that way.
  • Users with a passkey that enter their password will be denied, if you configure it that way.

jasonvarga avatar Apr 18 '24 19:04 jasonvarga

That sounds like a perfect solution. For example, I would enforce it by default. But maybe not if I’d use the 2fa addon. All options possible. I love it.

robdekort avatar Apr 18 '24 19:04 robdekort

Sounds good to me too. Let me know if you want me to update this to work that way.

ryanmitchell avatar Apr 18 '24 19:04 ryanmitchell

@jasonvarga I've updated this:

  • It now targets 5.x
  • It implements the allow_password_login_with_passkey config as suggested above
  • Passkeys no longer have a store, querybuilder or repository. In stache they are stored in the user yaml, in eloquent we query the model directly
  • I added some tests for adding and deleting passkeys (test coverage of the passkey functionality itself should come from the library not Statamic?)

ryanmitchell avatar Apr 23 '24 08:04 ryanmitchell

This flow seems really great, for anyone wondering what Ryan has done, I just test ran it and it uses the same flow as Github: Screenshot_2024-04-23_at_17 23 09

If you have allow_password_login_with_passkey set to true and you have setup a passkey, you can login using either options. If you have it set to false it blocks your login: Screenshot_2024-04-23_at_17 23 36

They only thing I've noticed is that when you enable oauth, you still get this middle step: Screenshot_2024-04-23_at_17 25 15

Which now isn't correct anymore. Perhaps this step could be removed and simplified to something like this? Screenshot_2024-04-23_at_17 27 13

robdekort avatar Apr 23 '24 15:04 robdekort

That sort of UI change is above my pay grade :)

ryanmitchell avatar Apr 23 '24 16:04 ryanmitchell

That sort of UI change is above my pay grade :)

I'll chip in £5

edalzell avatar Apr 23 '24 19:04 edalzell

Ok, I think I've got all the various states now. Happy to make more changes. This is what it looks like:

Normal login Screenshot 2024-04-24 at 09 16 42

Passkeys enabled Screenshot 2024-04-24 at 09 16 52

Passkeys and oAuth enabled Screenshot 2024-04-24 at 09 17 11

E-mail login disabled Screenshot 2024-04-24 at 09 17 20

Only oAuth enabled Screenshot 2024-04-24 at 09 17 28

robdekort avatar Apr 24 '24 07:04 robdekort

Some icons on those flat buttons might be the icing on the cake, but I don't have access to streamline.

robdekort avatar Apr 24 '24 07:04 robdekort