WebauthnComponents
contains a few modular components which may be combined to detect passkey support, register new keys, authenticate keys for existing users, and manage session tokens in the client.
See module documentation for each component for more detailed descriptions.
🧯 The following charts focus on the success path, where no error has ocurred.
sequenceDiagram
autonumber
participant Client
participant SupportComponent
participant ParentLiveView
participant RegistrationComponent
participant AuthenticationComponent
Client->>SupportComponent: "passkeys-supported"
SupportComponent->>ParentLiveView: `{:passkeys_supported, boolean}`
ParentLiveView->>RegistrationComponent: `@disabled = !@passkeys_supported`
ParentLiveView->>AuthenticationComponent: `@disabled = !@passkeys_supported`
A user wants to create a new account. If the user is already authenticated when they navigate to /sign-in
, the LiveView will redirect to /
.
sequenceDiagram
autonumber
actor User
participant Client
participant RegistrationComponent
participant ParentLiveView
User->>Client: Click `register`
Client->>RegistrationComponent: "register"
RegistrationComponent->>Client: "registration-challenge"
Client->>RegistrationComponent: "registration-attestation"
RegistrationComponent->>ParentLiveView: `{:registration_successful, ...}`
Once the parent LiveView receives the {:registration_successful, ...}
message, it must persist the %User{}
and %UserKey{}
. The wac.install
generator casts the new user key as an association in the user params, so both are created at once.
To keep the user signed in, the LiveView may create a session token.
A user wants to sign into an existing account. If the user is already authenticated when they navigate to /sign-in
, the LiveView will redirect to /
.
sequenceDiagram
autonumber
actor User
participant Client
participant AuthenticationComponent
participant ParentLiveView
User->>Client: Click `authenticate`
Client->>AuthenticationComponent: "authenticate"
AuthenticationComponent->>Client: "authentication-challenge"
Client->>AuthenticationComponent: "authentication-attestation"
AuthenticationComponent->>ParentLiveView: `{:find_credential, ...}`
ParentLiveView->>AuthenticationComponent: update: `%{user_keys: user_keys}`
AuthenticationComponent->>AuthenticationComponent: validate challenge against user_keys
AuthenticationComponent->>ParentLiveView: `{:authentication_successful, ...}`
Once the parent LiveView receives the {:find_credential, ...}
message, it must lookup the user via the user's existing key. To keep the user signed in, the LiveView may create a session token, Base64-encode the token, and pass it to TokenComponent
for persistence in the client's sessionStorage
.
A user has successfully registered or authenticated.
The LiveView will render a separate hidden form on the page, with a text input for the %UserToken{}
value. When this form is rendered, it will trigger a JS click on its own submit button, which will result in a POST
to /session
, protected by Phoenix's default CSRF protections (Plug docs).
The Session
controller then validates the token before updating the connection session and redirecting to /
upon success. If the token is invalid, the user will be redirected to /sign-in
with a flash message to sign in.
Successful Registration or Authentication
A user has successfully registered or authenticated.
sequenceDiagram
autonumber
participant ParentLiveView
participant TokenForm
participant HomePage
participant Router
ParentLiveView->>TokenForm: `@form[:value].value = b64_token`
TokenForm->>Session: submit POST to `/session`
Session->>Session: create/2
Session->>Session: lookup user by token
Session->>Session: update conn session
Session->>Router: redirect to `/`
Active Session
A user already has a valid session token.
sequenceDiagram
autonumber
participant Router
participant Session
Router->>Session: fetch_current_user/2
Session->>Session: get session token
Session->>Session: find user
Session->>Session: put user_id in session
Session->>Router: continue
Sign Out
A user has clicked the "Sign Out" button.
sequenceDiagram
autonumber
participant Client
participant SignOut
participant Router
Client->>SignOut: Click
SignOut->>Session: DELETE /session
Session->>Session: delete all session tokens for user_id
Session->>Session: clear the conn session
Session->>Router: redirect to `/`