Webauthn in CakePHP

I have been following the Webauthn standards and browser support since the early days of FIDO compatible keys. I strongly believe that hardware keys are our best path forward to provide phishing resistant, easy to operate authentication, that doesn’t compromise on privacy. I’ve used a hardware key as a MFA device for the past five years, and I greatly prefer it over SMS. It is slightly harder to use than password manager based TOTP or a mobile authenticator application. After having adapted to needing to use MFA on a daily basis, I enjoy hardware keys. However, I’m not the important market segment. The general public still does not use hardware keys. The gap in adoption isn’t due to the financial costs of hardware keys, as there are many affordable options. I believe the gap still exists because hardware keys are too hard to use. It is rare to see hardware keys outside of the workplace, or by individuals interested in technology and security. The recent passkeys announcement from Google renewed my interest in hardware keys though, as new standards (and implementations) are starting to deliver the usability improvements that are required to encourage mass market adoption of hardware keys.

What’s changed?

In short, the recent changes from Google and Apple enable syncing a passkey chain (a collection of passkeys) across your cloud account. This makes it easier to recover after a device is lost, or damaged. Furthermore, mobile phones can now use biometric or PIN authorization to act as a Webauthn compatible device. These changes should help to improve accessibility and expand the audience using passkeys. The cloud backups are a wonderful way to ensure continuity of passkeys consistent with what your cloud provider offers. I’m concerned that attaching passkeys to one’s cloud identity will further deepen the moats these vendors have. My hope is that the backend storage of passkeys is made extensible. I would love to see open source implementations spring up.

Exploring Webauthn

With my interest piqued again, I wanted to learn more. My favorite way to learn is to struggle through implementing it myself. I briefly considered implementing a client library from the first principles. I started reading the spec, and after getting part way through it, I became less interested in that challenge and was more interested in the application side of the solution. This led to me choosing lbuchs/WebAuthn as my client library. The API seemed reasonable, and there was an example application making example code and docs easy to find.

I focused on integrating Webauthn with a CakePHP application, and what that would look like as an authentication plugin Authenticator. I wanted to test out the abstractions in that plugin and validate that there weren’t any blocking issues for CakePHP applications to start supporting passkeys. I ended up creating an example application that implements a simple datamodel for users + passkeys. The application has user registration, login and key management functions, and a basic local development environment setup but not much else.

What went well

I did manage to get a working application. The lbuchs/WebAuthn library was great. The example application was a wonderful reference to have. I appreciate the focus on simplicity in the example application, it is a great reminder of what can be done without frameworks and libraries. The few times I did have to dive into the source, the docs were wonderful. I’m also happy that I didn’t find any blockers for implementing passkeys in a CakePHP application. I think the solution is well contained enough that it could be added incrementally to an application that is currently using passwords. This should allow an opt in flow for existing users, which I plan to explore soon.

What was hard

Webauthn and passkeys are significantly more complex than password based authentication. In order to develop, and deploy passkey support your server needs to:

1. Have HTTPS turned on. While this is generally easy in production environments, it can be more complicated locally. My example application used a simple solution of mkcert and stunnel, and there are many other solutions, but the complexity is higher than before.
2. You need to have additional hardware. You either need additional hardware, or a newer mobile device.
3. The concepts are more complicated. Instead of password hashing you need to have cursory knowledge in public & private key pairs, base64 encoding binary data, asynchronous JavaScript, the document.navigator API. This is far more concepts to understand compared to salted strong hashing like bcrypt.

Furthermore, because I was implementing the request serialization logic from first principles (because I like to do things the hard way). I bumped into several encoding mismatch errors that took time to debug. The parameters for when user-verification and user-required are set in different methods is similar, but not the same which tripped me up.

What’s to come

I would like to refine the data model interfaces a bit more, as the typing is weak, and the interfaces aren’t overly well thought out. After that, I’d like to build out a prototype of the opt-in to passkeys flow, to show how applications could help on-board their users to passkeys without giving up passwords for users not interested in change yet.


There are no comments, be the first!

Have your say: