defmodule WebAuthnComponents.PasskeyComponent do
@moduledoc """
LiveComponent for interacting with Passkeys.
> #### Caution {: .warning}
>
> This component should be considered in alpha status, with changes to the API, bugs, and incomplete documentation to be expected.
>
> It is **not** advisable to use this component in a production environment at this time.
## Overview
"Passkey" is an end-user-friendly moniker for the WebAuthentication API, aka WebAuthn, where credentials may be used **across devices** through a cloud synchronization mechanism. Support for Passkeys requires integration with a user's operating system, browser, and device. It also requires Javascript to be enabled in the browser since WebAuthn is a browser API, where credential creation and verification is performed.
Cloud synchronization is currently handled by the user's operating system - [Keychain](https://support.apple.com/guide/iphone/passkeys-passwords-devices-iph82d6721b2/ios) for MacOS users and [Google Password Manager](https://developers.google.com/identity/passkeys/supported-environments) for Android users. Third party services such as [1Password](https://www.future.1password.com/passkeys/) have also announced plans for Passkey support in upcoming releases.
## Terms
- **Registration**: The process of creating a new credential to be used for a new account in your application.
- **Authentication**: The process of using an existing credential to access an existing account in your application.
## A New Paradigm
With Passkeys, it may be challenging (pardon the pun) to understand how registration and authentication are performed. The model differs from traditional username + password authentication in significant ways.
This implementation of Passkeys takes advantage of userless or loginless authentication, where the user need not provide a username, email, password, or other data required for traditional registration. Instead, a UUID is provided to to the WebAuthn API along with other challenge data.
## Customization
The registration and authentications processes are handled by this LiveComponent, which includes both a `Register` and `Authenticate` button.
## Communication
Throughout the registration and authentication process, some messages must be passed to the parent LiveView. In the parent LiveView, use `handle_info/2` to accept the following messages:
- `{:passkeys_supported, boolean}`: If false, an error should be displayed to the user.
- `{:token_exists, token: token}`: Reports an existing session token. The parent application may decide whether to redirect to another view, clear the token, or render an error.
- `{:token_stored, token: token}`: Reports a token was successfully stored in the user's browser. The user should be redirected to another view at this point. For new users, it is recommended to proceed with profile setup since email and other details are not collected during registration.
- `{:token_cleared}`: Reports that a token was successfully cleared. A message _may_ be displayed to the user if it makes sense to do so.
- `{:registration_failure, message: message}`: Reports an error which should be displayed to the user. The parent application may display human-friendly verbiage instead, logging the error for internal debugging.
- `{:find_credentials, user_handle: user_handle}`: Reports a `user_handle` is requesting authentication. The parent application must return a matching user, if one exists, in order to proceed with authentication.
- `{:authentication_successful, key_id: raw_id, auth_data: auth_data}`: Reports the user was successfully authenticated by the WebAuthn API. The parent LiveView should create a new session token and pass it back to the component for persistence in the user's browser.
- `{:authentication_failure, message: message}`: Reports the provided user could not be authenticated, with a message that may be displayed or paraphrased for the user.
- `{:error, payload}`: Reports an error to the parent LiveView to be displayed or paraphrased for the user.
Errors are reported and should typically be rendered to the user via flash messages and/or logged in the application's error tracking system for analysis. The component passes these messages to the LiveView to allow complete control over visibility, appearance, and wording of errors.
## Tokens
TODO: Document token expectations and best practices.
"""
require Logger
use Phoenix.LiveComponent
@button_class "px-2 py-1 border border-gray-300 dark:border-gray-600 hover:border-transparent bg-gray-200 hover:bg-blue-600 hover:text-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 transition rounded text-base shadow-sm flex gap-2 items-center hover:-translate-y-px hover:shadow-md"
@div_class "flex gap-2 flex-wrap"
@support_error_class "flex gap-2 items-center justify-center font-bold w-full border-2 border-rose-500 text-rose-600 dark:text-rose-200 bg-rose-200 dark:bg-rose-800 rounded-md shadow-md p-4 mb-4 transition"
@timeout 60_000
@doc """
Mounts the component with default assigns.
## Configurable Assigns
The following assigns may be passed to the component from your LiveView:
- `@app`: _Required_ - The name of you application. May be a string or atom.
- `@button_class`: Styles for buttons in the component.
- `@div_class`: Styles for the `
` container for the buttons.
- `@support_error_class`: Styles for the `