WebauthnComponents.AuthenticationComponent (WebauthnComponents v0.11.3)

View Source

LiveComponent for authenticating a user with a WebAuthn credential.

Use this component to sign in an existing user or to re-authenticate during sensitive operations (security changes, destructive admin actions, and so on).

Assigns

  • @id (Required) An HTML element ID.
  • @mediation (Optional) "optional" (default) or "conditional".
  • @disabled (Optional) Disables the button. Defaults to false.
  • @class (Optional) CSS classes for the button.
  • @rest (Optional) Additional HTML attributes for the button.
  • @public_key_credential_request_options (Optional) A WebauthnComponents.PublicKeyCredentialRequestOptions struct controlling the request (for example, :timeout, :user_verification, and :allow_credentials).

Messages

  • %WebauthnComponents.FindUser{} — the user presented a credential. Reply with send_update(WebauthnComponents.AuthenticationComponent, id: ..., credentials: credentials).
  • {credential_id, %Wax.AuthenticatorData{}} — the credential was verified. credential_id is the raw credential id.
  • {:error, payload} — a client-side error. payload contains the message, name, and stack reported by the browser.
  • A Wax error struct (for example, %Wax.InvalidClientDataError{}) — server-side verification failed.

User Lookup

After the user approves authentication in the browser, the component sends a WebauthnComponents.FindUser.t/0 message to the parent LiveView. The host application must locate the user, apply any relevant validation (for example, an account-status check), and send the user's credentials back with Phoenix.LiveView.send_update/3. Each credential must expose a credential id and public key; by default the component reads the :id and :public_key fields from any struct or map. Define an implementation of WebauthnComponents.CredentialProtocol when your schema uses different field names.

Conditional UI

Set @mediation to "conditional" to render a hidden input that lets the browser offer a Passkey while the user interacts with the page, reducing the number of steps required to sign in.

For best results, use WebauthnComponents.ClientCapabilitiesComponent to detect browser support, then update @mediation with the appropriate value.

# MyAppWeb.Authentication template
<.live_component
  module={WebauthnComponents.ClientCapabilitiesComponent}
  id="client-capabilities-component"
/>

<.live_component
  module={WebauthnComponents.AuthenticationComponent}
  id="authentication-component"
  class="btn btn-primary w-full"
>
  <span>Log in with a Passkey</span>
</.live_component>
defmodule MyAppWeb.Authentication do
  ...

  def handle_info({:client_capabilities, capabilities}, socket) do
    if capabilities["conditionalGet"] do
      send_update(WebauthnComponents.AuthenticationComponent,
        id: "authentication-component",
        mediation: "conditional"
      )
    end

    {:noreply, socket}
  end
end

Example

defmodule MyAppWeb.Authentication do
  ...

  def handle_info(%WebauthnComponents.FindUser{user_handle: user_handle}, socket) do
    with {:ok, user} <- MyApp.Identity.get_user(user_handle),
         %MyApp.Identity.User{status: :active, credentials: credentials} <- user do
      send_update(WebauthnComponents.AuthenticationComponent,
        id: "authentication-component",
        credentials: credentials
      )

      {:noreply, socket}
    else
      error ->
        Logger.warning(error)
        
        {
          :noreply,
          socket
          |> put_flash(:error, "Authentication failed")
        }
    end
  end

  def handle_info({_credential_id, %Wax.AuthenticatorData{}}, socket) do
    # Authentication succeeded. Sign the user in.
    {:noreply, socket}
  end
end

In the template, render the component:

<.live_component
  id="authentication-component"
  module={WebauthnComponents.AuthenticationComponent}
  class="btn"
>
  Sign in with a Passkey
</.live_component>

Resources

Summary

Functions

handle_event(binary, credential, socket)

Callback implementation for Phoenix.LiveComponent.handle_event/3.

mount(socket)

Callback implementation for Phoenix.LiveComponent.mount/1.

render(assigns)

Callback implementation for Phoenix.LiveComponent.render/1.

update(assigns, socket)

Callback implementation for Phoenix.LiveComponent.update/2.