WebAuthnLiveComponent - Passwordless Auth for LiveView Apps

WebAuthnLiveComponent WebAuthnComponents

See this post about renaming the package.

Passwordless authentication for Phoenix LiveView applications.

Description

I am happy to announce public availability of WebAuthnComponents, an implementation of passwordless authentication designed for LiveView applications.

As Passkey support is rolled out to operating systems, browsers, and credential managers, WebAuthnComponents will allow you to easily add passwordless authentication to greenfield and brownfield applications.

This package is released as part of my talk at ElixirConf 2022. As of September 1, 2022, it is in an early alpha stage, and I am looking for feedback from early adopters. See the readme for caveats and plans for v1.

Updates

Resources

34 Likes

The presentation was great. Cant wait to test this out

1 Like

Thank you @andreaseriksson! I’ll be updating documentation over the weekend so it’s clear what schemas and code you need to get things up and running.

1 Like

Is there a recording of your talk?

I would like to see. Tried searching on Youtube to no avail. :sweat_smile:

2 Likes

The virtual talks were uploaded to the ElixirConf YouTube channel recently, and I suspect the in-person talks will be uploaded in the next few weeks. Until then, they’re only available through Vito with a paid pass.

1 Like

Surprise! The video was uploaded just a couple hours ago. :tada:

10 Likes

I’m so lucky, I started searching at such a time with so many coincidences.

I’m surprised!!!

Thank you, will watch it today. :grin:


I watched it. It’s awesome.

I will use it tonight.

1 Like

Great talk! It’s nice to see Wax being used in such a neat way :slight_smile:

Note that I’ve released some very needed wax_ updates recently:

[0.6.0] - 2022-11-07

Changed

  • [Wax] Wax.new_authentication_challenge/1 signature change to support resident keys
  • [Wax] Wax.authenticate/6 signature change to support resident keys

[0.5.0] - 2022-11-04

Changed

  • [Wax] Requires OTP25+
  • [Wax] Supports Apple Anonymous attestation
  • [Wax] Returns metadata as a map conforming with FIDO Metadata Statement, and no longer Elixir structs
  • [Wax] All returned errors are exceptions
  • [Wax.Metadata] Wax now loads MDSv3 metadata, and no longer requires an access token
  • [Wax.Metadata] The tesla library is no longer used

Using discoverable credentials (resident keys) is now supported. That enables loginless login:

wax_demo has been updated accordingly.

3 Likes

Excellent! I’ll update Wax and check out the new features. :pray: :pray:

3 Likes

Now that the talk is publicly available, and life events have simmered down, I will be working on updates to the Hex package. I hope to have a new version within a week. :crossed_fingers:

3 Likes

Phoenix 1.7 rc is out as well. Perhaps now you can update the LiveView to 0.18 and remove those commented out slots you have in your LiveComponent.

Maybe even try out the gen.auth.live, because It would be awesome if your WebAuthnLiveComponent can play nice with the generated code. :smile:


Also checkout phx_live_storybook! It would be great if the component you make is presentable in that view, because it will eventually get added to Phoenix 1.7 and It would be great to see running components in Storybook mode. As it makes it easy to explore.

Yesterday I searched around the Git repo for the component, unable to find any documentation or example code to run the WebAuthn component.

What’s better than a running live documentation inside Storybook.

1 Like

Update :sparkles:

Still alpha after all these years

I have updated WebAuthnLiveComponent with a new version which adds preliminary support for Passkeys. More specifically, discoverable credentials aka resident keys.

  • WebAuthnLiveComponent.PasskeyComponent is the module containing the new LiveComponent, which provides a drop-in pair of buttons for registration and authentication via Passkey in a LiveView application
  • passkey.js contains the client-side Javascript for interacting with the WebAuthn API, sending events back to the PasskeyComponent, which in turn sends messages to the parent LiveView (see Communication).

Thanks to @dbern for moral support and asking great questions as I wrestled with implementing Passkey support. The mental model here is quite different from traditional authentication, so it was helpful to have someone to ask “how,” “why,” and “what if” throughout the process.

What’s Next? :building_construction:

Split, merge, or ???

The full module name, WebAuthnLiveComponent.PasskeyComponent is just a bit too much for my taste. I expect confusion about the two LiveComponents in this package, which are based on the same WebAuthn API, but take two approaches to implementation. The original component includes a form which requires a username before proceeding with registration or authentication.

So, I am looking for feedback on whether to split PasskeyComponent into its own Hex package, replace the WebAuthnLiveComponent module with the Passkey implementation, or something else. I am inclined to keep the original component for applications which would like to require a username first, though I would plan on replacing the form with a slot and using a @changeset assign to allow more control from the parent LiveView.

This is still an early alpha package with little adoption, but some excitement and interest expressed in this forum. So, there is still time to make significant changes to improve developer experience.

Speaking of Developer Experience :construction_worker_woman:

I spent some time documenting the PasskeyComponent module, but there is still more work required to clearly document the process of using the component from start to finish. This includes adding the JS hook to the LiveSocket and several message handlers that must be added to the parent LiveView. Not to mention requirements for persisting the user’s public keys created during registration, as well as other requirements and best practices in the parent application.

Demo Application

Updates to the demo Phoenix application are a work in progress. With the Hex package updated, some cleanup needs to be done to remove prototype component code. Other updates to the demo app include:

  • Finish Live Session Hook implementation
  • Add /passkey link to landing page
  • Add tests for the Passkey LiveView
  • Remove Auth LiveView and route
  • ...

Contributions :handshake:

If you are interested in contributing, please don’t hesitate to respond in this thread. Since this is a potentially critical piece of security code, I believe it is important to keep the conversations as public as possible. :pray:

7 Likes

Update

Getting beta all the time

The demo application has been updated with an implementation of the PasskeyComponent. The PR is hefty, in part due to some Tailwind auto sorting, but the code demonstrates registration, authentication, database design, and token persistence in the client and server.

What’s Included

  • DemoWeb.Hooks.User includes two hooks for a) populating the @current_user assign and b) requiring a @current_user assign. See the router for example usage.
  • There are 9 messages passed to the parent LiveView via handle_info as the component coordinates with the application.
  • Users are automatically redirected to the user profile view upon successful registration and authentication.
  • Errors are passed from the component to the parent LiveView for customization and presentation.
  • The following schemas were added or updated:
    • Demo.Accounts.User
    • Demo.Accounts.UserProfile
    • Demo.Authentication.UserKey
    • Demo.Authentication.UserToken

Database Design

With this implementation of discoverable credentials, I opted to register users without username or email. These fields were moved into Demo.Accounts.UserProfile. As a result the Demo.Accounts.User schema is quite bare.

The only downside I see with this approach is that the username is not stored in the user’s Passkey, which instead stores the component’s @app assign. I will be looking into whether existing passkeys may be renamed through the application.

Test Drive

With the demo application and component package updated, I am ready for feedback. With the hardest problems seemingly solved, I plan on improving documentation in both repos.

If you have a few minutes to review the code, launch the demo app, or even attempt implementation in your own application, I would greatly appreciate your feedback.

6 Likes

It’s so beautiful.

Observations:

  1. When I tabbed into the email field in User Profile page, my cursor didn’t show up the first time. (I don’t know how the focus got messed up)
  2. I wish the sign in button changed in the first page to sign out.
  3. There’s a flicker, when signing out & signing in.
  4. The toast message that shows up after successful sign in disappears because of the flicker.
  5. Also, instead of using UUID, can it use KSUID, or we can change it later. :sweat_smile:

P.S. Can the demo contain the phx.gen.auth, because I want to make sense of how both of them can be used together.


P.P.S. This is awesome. More people should get in on this.

3 Likes

Thank you, @derpycoder. I’ve opened PR 8 to address this feedback. I’ll leave the PR open until tomorrow evening to allow time to review if you’re interested.

  1. I added autofocus to the first field in the user profile view.
    a. The disappearing cursor may have been related to some issues with the user hook (#3 below).
  2. The home page’s Sign In button is now My Profile, which links to /user/profile.
  3. The user hook was updated to remove an unnecessary redirect and to clean up the code a bit.
    a. Did this reduce/eliminate page flicker?
  4. Flashes have been removed for now, and I’m trying to understand if/how they can be persisted across redirects.
  5. Thanks for introducing me to KSUIDs.
    a. UUIDs are supported out of the box by Phoenix and Postgres.
    b. The demo app is just that, a demo app. KSUIDs might be cool, but the purpose of the demo app is to illustrate how the passkey component can be implemented.

I’ve heard your feedback about phx.gen.auth. For now, my focus is on getting the implementation right for fresh Phoenix apps. My thinking is that passkeys are a replacement for traditional auth, though it’s likely most production apps will want a fallback to basic auth when passkeys are not supported. I have to review the auth generators and think about how to support both modes of authentication.

2 Likes

That’s awesome. :smiley:


Yup, I don’t want you to add everything under the sun. I was just unsure if it’s possible to add it later as well.

Same here, I just want to have both: for fallback as well as present it as 2nd factor or 3rd and finally, I want to see how they play together.


I have yet to receive a YubiKey, hope to try that with this component.


I will find some time to go through the code today. :upside_down_face:

3 Likes

Update

Thank you to everyone who has tried the updated component - it’s great to see the interest. Although interest is fairly low, it’s early days, and I’m glad to have the flexibility to make significant changes discussed below.

My next updates will add telemetry and improve documentation for this somewhat convoluted authentication process. The public project shows completed and planned work for this repo.

RE: MFA

This clarifies things a bit. Now I understand you are also looking for multi factor authentication (MFA) for apps using phx.gen.auth or other implementations of traditional authentication.

Passkeys should be considered a more secure alternative to traditional authentication:

Based on industry standards for account authentication, passkeys are easier to use than passwords and far more secure. Adopt passkeys to give people a simple, secure way to sign in to your apps and websites across platforms — with no passwords required.

Passkeys Overview - Apple Developer

Once I am updating documentation, I will attempt to clearly distinguish between Passkeys and MFA.

For basic auth + MFA, WebAuthn does provide a better means of securing accounts than SMS, email, or one time password codes. However, I believe this would require a separate component since the flows are a bit different.

Multiple Keys

Your question is in line with thoughts I was having towards the end of the demo app update:

What if a user wants to add a secondary/backup/family/survivor key?

The answer seems to be splitting up the registration and authentication buttons into separate components. By doing so, it may be easier to support MFA for apps using traditional auth:

  • The registration component would need to accept user data.
  • It’s not clear yet whether the dedicated authentication component would also work well for MFA, or whether a separate component would be necessary.

Hello Again, World!

With this shift in component design, the time has come to retire the webauthn_live_component package and introduce webauthn_components. My hope is that this new package would be more flexible to the implementations we’ve discussed, possibly others.

5 Likes

Update

I have opened PR 37 with the primary goals of breaking apart the components and improving documentation.

This seemed more daunting than it turned out to be. I’m finding the separated components easier to understand and document, and I hope you agree.

  • The README now includes Mermaid diagrams illustrating how each component interacts with the client and parent LiveView.
  • Each component has more complete module documentation with standardized sections for assigns, events, and messages.
  • A new USAGE.md was added, and I plan on filling in more detail before closing the PR.

Feedback is welcome, so feel free to review the code and documentation to check for errors and clarity. I request that questions and comments for the PR be added to the PR instead of the forum for posterity.

What’s Next

You may notice that I’ve upgraded this repo from early alpha to early beta in the readme. The new design and documentation are key :smirk: to maintainability and adoption. Testing is also critical for a set of security-oriented components, and the modular design should make testing more straightforward than it would have been.

After this PR is merged, I plan to proceed with writing tests for each component. Last time I attempted to write tests, I discovered that there was little or no tooling for testing JS hooks. I believe the components may be tested using factories for users, user keys, and user tokens, but I’m not sure how to proceed with JS testing.

If you are interested in contributing to component and/or JS tests, let me know here or via DM so we can discuss. :pray:

5 Likes

Update: v0.3.1 Released

PR 37 has been closed, with a major overhaul of the components. Here are the headlines:

  • Components have been split up for better modularity
  • The package has been renamed to WebauthnComponents / webauthn_components
  • Mermaid diagrams were added to the readme
  • Preliminary tests were added

Separately, a branch is open for the demo repo, which has been renamed to webauthn_components_demo to factor in changes from the Hex package.

Links

4 Likes

Update

The webauthn_components_demo repo has been updated with the new component implementation and a few developer experience improvements.

  1. Updated component implementation (PR)
  2. Added Docker development container (code)
  3. Update the readme (file)

With the new development container, it should be easier to get up and running since a Postgres container is included. The readme provides instructions for starting the app with or without a dev container.

What’s Next

Because Passkey authentication is a big departure from traditional auth, I am planning to record a brief video demonstrating user flows and database storage. This should make adoption less daunting.

The remaining todos for this project are mostly related to developer experience. First, I plan to implement Telemetry in the components package so parent applications can attach to relevant events. Then, I want to investigate creating generators for some of the tedious code required to fully implement Passkey support in a Phoenix application.

Getting Involved

In the meantime, I hope to start getting feedback from LiveView developers to refine the components and demo app, with the goal of building confidence for a 1.0 release.

Feel free to try out the demo app, provide feedback here, and share with anyone who may be interested in this project. :pray:

6 Likes