### Create TOTP Setup LiveView Source: https://github.com/team-alembic/ash_authentication_phoenix/blob/main/documentation/tutorials/totp.md Implement a LiveView to render the TOTP setup component for authenticated users. ```elixir # lib/my_app_web/live/totp_setup_live.ex defmodule MyAppWeb.TotpSetupLive do use MyAppWeb, :live_view def mount(_params, _session, socket) do {:ok, strategy} = AshAuthentication.Info.strategy(MyApp.Accounts.User, :totp) {:ok, assign(socket, strategy: strategy)} end def render(assigns) do ~H"""

Set Up Two-Factor Authentication

<.live_component module={AshAuthentication.Phoenix.Components.Totp.SetupForm} id="totp-setup" strategy={@strategy} current_user={@current_user} />
""" end end ``` -------------------------------- ### Start Phoenix Server Command Source: https://github.com/team-alembic/ash_authentication_phoenix/blob/main/documentation/tutorials/get-started.md This command starts the Phoenix development server. Visit `localhost:4000` in your browser to see the application. ```bash mix phx.server ``` -------------------------------- ### Add TOTP Setup Route Source: https://github.com/team-alembic/ash_authentication_phoenix/blob/main/documentation/tutorials/totp.md Register the setup LiveView in the router, ensuring it is protected by authentication. ```elixir scope "/", MyAppWeb do pipe_through [:browser, :require_authenticated_user] live "/settings/totp", TotpSetupLive end ``` -------------------------------- ### Install Ash Authentication Phoenix with Igniter Source: https://github.com/team-alembic/ash_authentication_phoenix/blob/main/documentation/tutorials/get-started.md Use the Igniter installer to automatically configure the project for Ash Authentication. ```sh mix igniter.install ash_authentication_phoenix ``` ```sh mix igniter.install ash_authentication # and then run mix igniter.install ash_authentication_phoenix ``` -------------------------------- ### Configure TOTP Two-Factor Authentication Routes Source: https://context7.com/team-alembic/ash_authentication_phoenix/llms.txt Set up routes for TOTP verification and setup pages. Ensure the user is authenticated for the TOTP setup. ```elixir defmodule MyAppWeb.Router do use MyAppWeb, :router use AshAuthentication.Phoenix.Router scope "/", MyAppWeb do pipe_through :browser # TOTP verification page (after password sign-in) totp_2fa_route MyApp.Accounts.User, :totp, path: "/totp-verify", auth_routes_prefix: "/auth" # TOTP setup page (for authenticated users) ash_authentication_live_session :totp_setup, on_mount: {MyAppWeb.LiveUserAuth, :live_user_required} do totp_setup_route MyApp.Accounts.User, :totp, path: "/totp-setup", auth_routes_prefix: "/auth" end end end ``` -------------------------------- ### Add TOTP Setup Route Source: https://github.com/team-alembic/ash_authentication_phoenix/blob/main/documentation/tutorials/totp-2fa.md Define a route for the TOTP setup LiveView page within your Phoenix router. This uses `ash_authentication_live_session` to ensure the user is authenticated before accessing the setup page. ```elixir scope "/", MyAppWeb do pipe_through :browser ash_authentication_live_session :authenticated, on_mount: [{MyAppWeb.LiveUserAuth, :live_user_required}] do live "/auth/totp/setup", TotpSetupLive end end ``` -------------------------------- ### Authentication Metadata Examples Source: https://github.com/team-alembic/ash_authentication_phoenix/blob/main/documentation/tutorials/totp-2fa.md Illustrates how authentication strategies are recorded in user metadata after sign-in and how TOTP verification timestamps are stored. ```elixir # After password sign-in user.__metadata__.authentication_strategies #=> [:password] # After TOTP sign-in user.__metadata__.authentication_strategies #=> [:totp] # After TOTP verification (includes timestamp) user.__metadata__.totp_verified_at #=> ~U[2024-01-15 10:30:00Z] ``` -------------------------------- ### Configure Router with Authentication Hooks Source: https://github.com/team-alembic/ash_authentication_phoenix/blob/main/documentation/tutorials/liveview.md Integrate the `LiveUserAuth` hooks into your Phoenix router to enforce authentication requirements for specific routes. This example shows required and optional authentication setups. ```elixir # lib/my_app_web/router.ex # ... scope "/", MyAppWeb do # ... ash_authentication_live_session :authentication_required, on_mount: {MyAppWeb.LiveUserAuth, :live_user_required} do live "/protected_route", ProjectLive.Index, :index end ash_authentication_live_session :authentication_optional, on_mount: {MyAppWeb.LiveUserAuth, :live_user_optional} do live "/", ProjectLive.Index, :index end end # ... ``` -------------------------------- ### Create TOTP Setup LiveView Page Source: https://github.com/team-alembic/ash_authentication_phoenix/blob/main/documentation/tutorials/totp-2fa.md Implement a LiveView page for users to set up Two-Factor Authentication (TOTP). This involves defining the `mount` function to fetch the TOTP strategy and rendering a `SetupForm` component. Ensure the `AshAuthentication.Phoenix.Components.Totp.SetupForm` module is aliased. ```elixir # lib/my_app_web/live/totp_setup_live.ex defmodule MyAppWeb.TotpSetupLive do use MyAppWeb, :live_view alias AshAuthentication.Phoenix.Components.Totp.SetupForm def mount(_params, _session, socket) do {:ok, strategy} = AshAuthentication.Info.strategy(MyApp.Accounts.User, :totp) {:ok, assign(socket, strategy: strategy)} end def render(assigns) do ~H"""

Set Up Two-Factor Authentication

<.live_component module={SetupForm} id="totp-setup" strategy={@strategy} current_user={@current_user} />
""" end end ``` -------------------------------- ### TOTP Authentication Components Source: https://github.com/team-alembic/ash_authentication_phoenix/blob/main/documentation/tutorials/ui-overrides.md Configuration parameters for TOTP setup and verification LiveView pages and components. ```APIDOC ## AshAuthentication.Phoenix.TotpSetupLive ### Description A generic, white-label TOTP setup page for configuring two-factor authentication. ### Parameters #### Attributes - **error_class** (string) - Optional - CSS class for the error message when user is not authenticated. - **root_class** (string) - Optional - CSS class for the root div element. - **totp_setup_id** (string) - Optional - Element ID for the TotpSetup LiveComponent. ## AshAuthentication.Phoenix.TotpVerifyLive ### Description A generic, white-label TOTP verification page for two-factor authentication. ### Parameters #### Attributes - **root_class** (string) - Optional - CSS class for the root div element. - **totp_verify_id** (string) - Optional - Element ID for the TotpVerify LiveComponent. ## AshAuthentication.Phoenix.Components.Totp.Verify2faForm ### Description Generates a verification form for TOTP two-factor authentication. ### Parameters #### Attributes - **button_text** (string) - Optional - Text for the submit button. - **disable_button_text** (string) - Optional - Text for the submit button when the request is happening. - **error_class** (string) - Optional - CSS class for error messages. - **form_class** (string) - Optional - CSS class for the form element. - **instructions_class** (string) - Optional - CSS class for instructions text. - **instructions_text** (string) - Optional - Instructions text shown above the code input. - **label_class** (string) - Optional - CSS class for the h2 element. - **label_text** (string) - Optional - Text for the form heading. - **recovery_code_link_class** (string) - Optional - CSS class for the recovery code link. - **recovery_code_link_path** (string) - Optional - Path to the recovery code verification page. - **recovery_code_link_text** (string) - Optional - Text for the link to recovery code verification. - **root_class** (string) - Optional - CSS class for the root div element. - **sign_in_link_class** (string) - Optional - CSS class for the sign-in link when not authenticated. - **sign_in_link_text** (string) - Optional - Text for the sign-in link. - **slot_class** (string) - Optional - CSS class for the div surrounding the slot. ``` -------------------------------- ### Implement LiveView Authentication Callbacks Source: https://context7.com/team-alembic/ash_authentication_phoenix/llms.txt Define `on_mount` callbacks in a LiveView module to handle user authentication logic. This example shows how to manage optional and required authentication states, redirecting users when necessary. ```elixir defmodule MyAppWeb.LiveUserAuth do import Phoenix.Component use MyAppWeb, :verified_routes def on_mount(:live_user_optional, _params, _session, socket) do if socket.assigns[:current_user] do {:cont, socket} else {:cont, assign(socket, :current_user, nil)} end end def on_mount(:live_user_required, _params, _session, socket) do if socket.assigns[:current_user] do {:cont, socket} else {:halt, Phoenix.LiveView.redirect(socket, to: ~p"/sign-in")} end end def on_mount(:live_no_user, _params, _session, socket) do if socket.assigns[:current_user] do {:halt, Phoenix.LiveView.redirect(socket, to: ~p"/")} else {:cont, assign(socket, :current_user, nil)} end end end ``` -------------------------------- ### Use Role-Based Access in LiveView Source: https://github.com/team-alembic/ash_authentication_phoenix/blob/main/documentation/tutorials/liveview.md Apply role-based access control within a LiveView's `on_mount` callback. This example demonstrates how to specify required roles for accessing a LiveView. ```elixir on_mount {MyAppWeb.LiveUserAuth, {:required_role: :admin}} ``` ```elixir on_mount {MyAppWeb.LiveUserAuth, {:required_roles: [:admin, :staff]}} ``` -------------------------------- ### Define TOTP Routes in Phoenix Router Source: https://github.com/team-alembic/ash_authentication_phoenix/blob/main/documentation/tutorials/totp-2fa.md Use the totp_2fa_route and totp_setup_route macros to expose verification and setup pages. The set_actor plug is required in the browser pipeline for these routes to function. ```elixir defmodule MyAppWeb.Router do use MyAppWeb, :router use AshAuthentication.Phoenix.Router pipeline :browser do plug :accepts, ["html"] plug :fetch_session plug :fetch_live_flash plug :put_root_layout, html: {MyAppWeb.Layouts, :root} plug :protect_from_forgery plug :put_secure_browser_headers plug :load_from_session plug :set_actor, :user end scope "/", MyAppWeb do pipe_through :browser # Standard auth routes auth_routes AuthController, MyApp.Accounts.User, path: "/auth" # TOTP verification route - defaults to /totp-verify totp_2fa_route MyApp.Accounts.User, :totp, auth_routes_prefix: "/auth" # TOTP setup route - defaults to /totp-setup totp_setup_route MyApp.Accounts.User, :totp, auth_routes_prefix: "/auth" end end ``` -------------------------------- ### Redirect to Recovery Codes After TOTP Setup Source: https://github.com/team-alembic/ash_authentication_phoenix/blob/main/documentation/tutorials/recovery-codes.md Implement a success clause in the auth controller to redirect users to the recovery codes page after completing TOTP setup. ```elixir # Added automatically by the igniter def success(conn, {_, :confirm_setup}, user, token) do conn |> store_in_session(user) |> set_live_socket_id(token) |> assign(:current_user, user) |> redirect(to: ~p"/recovery-codes") end ``` -------------------------------- ### Configure Recovery Code Plug Source: https://github.com/team-alembic/ash_authentication_phoenix/blob/main/documentation/tutorials/recovery-codes.md Use this plug in controller pipelines to enforce recovery code setup for users. ```elixir pipeline :require_recovery_codes do plug AshAuthentication.Phoenix.Plug.RequireRecoveryCodes, resource: MyApp.Accounts.User, on_missing: :redirect_to_setup, setup_path: "/recovery-codes" end ``` -------------------------------- ### Password Strategy Metadata Structure Source: https://github.com/team-alembic/ash_authentication_phoenix/blob/main/documentation/topics/authentication-metadata.md Example of the metadata map generated when using the password authentication strategy. ```elixir %{ authentication_strategies: [:password], token: "..." } ``` -------------------------------- ### TOTP Strategy Metadata Structure Source: https://github.com/team-alembic/ash_authentication_phoenix/blob/main/documentation/topics/authentication-metadata.md Example of the metadata map generated when using the TOTP authentication strategy. ```elixir %{ authentication_strategies: [:totp], totp_verified_at: ~U[2024-01-15 10:30:00Z], token: "..." } ``` -------------------------------- ### Implement Authentication Callbacks with AshAuthentication.Phoenix.Controller Source: https://context7.com/team-alembic/ash_authentication_phoenix/llms.txt Define success and failure callbacks to handle authentication outcomes. This example shows a controller-backed flow redirecting to a return path or sign-in page. ```elixir defmodule MyAppWeb.AuthController do use MyAppWeb, :controller use AshAuthentication.Phoenix.Controller def success(conn, _activity, user, token) do return_to = get_session(conn, :return_to) || ~p"/" conn |> delete_session(:return_to) |> store_in_session(user) |> assign(:current_user, user) |> set_live_socket_id(token) |> redirect(to: return_to) end def failure(conn, _activity, _reason) do conn |> put_flash(:error, "Incorrect email or password") |> redirect(to: ~p"/sign-in") end def sign_out(conn, _params) do return_to = get_session(conn, :return_to) || ~p"/" conn |> clear_session(:my_app) |> redirect(to: return_to) end end ``` -------------------------------- ### GET /recovery-code-verify/:token or /recovery-code-verify Source: https://github.com/team-alembic/ash_authentication_phoenix/blob/main/documentation/tutorials/recovery-codes.md Generates a recovery code verification page. Supports token-based verification for 2FA fallback after password sign-in, or step-up verification for already-authenticated users. ```APIDOC ## GET /recovery-code-verify/:token or /recovery-code-verify ### Description Generates a recovery code verification page. Supports two modes: Token-based (used after password sign-in as a 2FA fallback) and Step-up (used when an already-authenticated user needs to re-verify their identity). ### Method GET ### Endpoint /recovery-code-verify/:token or /recovery-code-verify ### Parameters #### Path Parameters - **token** (string) - Optional - Used in token-based mode after password sign-in. ``` -------------------------------- ### Customize Authentication UI Components Source: https://context7.com/team-alembic/ash_authentication_phoenix/llms.txt Use the `AshAuthentication.Phoenix.Overrides` module to customize CSS classes, text, and image URLs for authentication components. This example overrides banner and password form elements. ```elixir defmodule MyAppWeb.AuthOverrides do use AshAuthentication.Phoenix.Overrides alias AshAuthentication.Phoenix.Components # Customize the banner image override Components.Banner do set :image_url, "/images/my-logo.png" set :dark_image_url, "/images/my-logo-dark.png" set :href_url, "/" end # Customize password form appearance override Components.Password do set :sign_in_toggle_text, "Already have an account? Sign in" set :register_toggle_text, "Don't have an account? Register" set :reset_toggle_text, "Forgot password?" end # Customize input styling override Components.Password.Input do set :input_class, "w-full px-4 py-2 border rounded-lg focus:ring-2" set :label_class, "block text-sm font-medium mb-1" set :submit_class, "w-full bg-blue-600 text-white py-2 rounded-lg" set :identity_input_label, "Email Address" set :password_input_label, "Your Password" end # Customize sign-in form text override Components.Password.SignInForm do set :button_text, "Log In" set :disable_button_text, "Logging in..." end end ``` -------------------------------- ### Configure Router for TOTP Source: https://github.com/team-alembic/ash_authentication_phoenix/blob/main/documentation/tutorials/totp.md Use standard authentication route macros to enable TOTP sign-in. ```elixir scope "/", MyAppWeb do pipe_through :browser auth_routes AuthController, MyApp.Accounts.User, path: "/auth" sign_in_route(auth_routes_prefix: "/auth") end ``` -------------------------------- ### GET /recovery-codes Source: https://github.com/team-alembic/ash_authentication_phoenix/blob/main/documentation/tutorials/recovery-codes.md Generates a page for generating and displaying recovery codes. This route should be protected by authentication middleware. ```APIDOC ## GET /recovery-codes ### Description Generates a page for generating and displaying recovery codes. This should be placed behind authentication middleware so only authenticated users can access it. ### Method GET ### Endpoint /recovery-codes ``` -------------------------------- ### Load Authenticated Users with Plugs Source: https://context7.com/team-alembic/ash_authentication_phoenix/llms.txt Use plug helpers to load users from sessions or bearer tokens, and to store users in the session. These plugs are typically configured in your router pipelines. ```elixir defmodule MyAppWeb.Router do use MyAppWeb, :router use AshAuthentication.Phoenix.Router pipeline :browser do plug :accepts, ["html"] plug :fetch_session # Load user from session token plug :load_from_session # Optionally sign in with remember me cookie plug :sign_in_with_remember_me end pipeline :api do plug :accepts, ["json"] # Load user from Authorization header bearer token plug :load_from_bearer # Set the Ash actor for authorization plug :set_actor, :user end # In a controller or plug, store user in session: # conn |> store_in_session(user) # Revoke tokens on sign out: # conn |> revoke_bearer_tokens() # conn |> revoke_session_tokens() end ``` -------------------------------- ### Configure LiveView Routes with AshAuthentication Source: https://context7.com/team-alembic/ash_authentication_phoenix/llms.txt Use the `ash_authentication_live_session` macro in your router to define routes that require authentication or where authentication is optional. The `on_mount` option specifies how to handle user authentication status. ```elixir defmodule MyAppWeb.Router do use MyAppWeb, :router use AshAuthentication.Phoenix.Router scope "/", MyAppWeb do pipe_through :browser # Routes requiring authentication ash_authentication_live_session :authenticated, on_mount: {MyAppWeb.LiveUserAuth, :live_user_required} do live "/dashboard", DashboardLive, :index live "/settings", SettingsLive, :index end # Routes where auth is optional ash_authentication_live_session :public, on_mount: {MyAppWeb.LiveUserAuth, :live_user_optional} do live "/", HomeLive, :index end end end ``` -------------------------------- ### Implement 2FA Grace Period in Phoenix LiveView Source: https://github.com/team-alembic/ash_authentication_phoenix/blob/main/documentation/tutorials/totp-2fa.md Use this module to allow users a grace period to set up 2FA. It checks if 2FA is configured or if the grace period is still active before proceeding. ```elixir defmodule MyAppWeb.Require2FAWithGrace do import Phoenix.Component import Phoenix.LiveView alias AshAuthentication.Phoenix.TotpHelpers @grace_period_days 7 def on_mount(:require_2fa_with_grace, _params, _session, socket) do user = socket.assigns[:current_user] cond do is_nil(user) -> {:halt, redirect(socket, to: "/sign-in")} TotpHelpers.totp_configured?(user) -> {:cont, assign(socket, :totp_configured, true)} within_grace_period?(user) -> socket = socket |> assign(:totp_configured, false) |> assign(:grace_period_remaining, days_remaining(user)) |> put_flash(:warning, "Please set up 2FA within #{days_remaining(user)} days") {:cont, socket} true -> socket = socket |> put_flash(:error, "Two-factor authentication is now required") |> redirect(to: "/auth/totp/setup") {:halt, socket} end end defp within_grace_period?(user) do days_remaining(user) > 0 end defp days_remaining(user) do created = user.inserted_at deadline = DateTime.add(created, @grace_period_days, :day) DateTime.diff(deadline, DateTime.utc_now(), :day) |> max(0) end end ``` -------------------------------- ### Implement AuthController for Authentication Callbacks Source: https://github.com/team-alembic/ash_authentication_phoenix/blob/main/documentation/tutorials/get-started.md Create a controller to handle success, failure, and sign-out actions for authentication flows. ```elixir defmodule ExampleWeb.AuthController do use ExampleWeb, :controller use AshAuthentication.Phoenix.Controller def success(conn, _activity, user, _token) do return_to = get_session(conn, :return_to) || ~p"/" conn |> delete_session(:return_to) |> store_in_session(user) # If your resource has a different name, update the assign name here (i.e :current_admin) |> assign(:current_user, user) |> redirect(to: return_to) end def failure(conn, _activity, _reason) do conn |> put_flash(:error, "Incorrect email or password") |> redirect(to: ~p"/sign-in") end def sign_out(conn, _params) do return_to = get_session(conn, :return_to) || ~p"/" conn |> clear_session(:my_app) |> redirect(to: return_to) end end ``` -------------------------------- ### Multi-Factor Authentication Metadata Accumulation Source: https://github.com/team-alembic/ash_authentication_phoenix/blob/main/documentation/topics/authentication-metadata.md Example of the metadata map showing accumulated strategies after both password and TOTP verification. ```elixir # After password + TOTP verification %{ authentication_strategies: [:password, :totp], totp_verified_at: ~U[2024-01-15 10:30:00Z], token: "..." } ``` -------------------------------- ### Generated Auth Controller Clauses for 2FA Source: https://github.com/team-alembic/ash_authentication_phoenix/blob/main/documentation/tutorials/totp-2fa.md These clauses handle the 2FA flow during sign-in and registration. They check TOTP configuration and redirect users to the appropriate setup or verification pages. ```elixir defmodule MyAppWeb.AuthController do use MyAppWeb, :controller use AshAuthentication.Phoenix.Controller # Sign-in interception: check if TOTP is configured def success(conn, {_, phase} = _activity, user, token) when phase in [:sign_in, :sign_in_with_token] do return_to = get_session(conn, :return_to) || ~p"/" if AshAuthentication.Phoenix.TotpHelpers.totp_configured?(user) do # TOTP is set up — store user in session and redirect to verify conn |> store_in_session(user) |> set_live_socket_id(token) |> assign(:current_user, user) |> put_session(:return_to, return_to) |> redirect(to: ~p"/totp-verify/#{token}") else # No TOTP configured — redirect to setup conn |> store_in_session(user) |> set_live_socket_id(token) |> assign(:current_user, user) |> put_session(:return_to, return_to) |> redirect(to: ~p"/totp-setup") end end # Registration: always redirect to TOTP setup def success(conn, {_, :register}, user, token) do return_to = get_session(conn, :return_to) || ~p"/" conn |> store_in_session(user) |> set_live_socket_id(token) |> assign(:current_user, user) |> put_session(:return_to, return_to) |> redirect(to: ~p"/totp-setup") end # Default catch-all for other auth activities def success(conn, activity, user, token) do # ... standard success handling end # ... end ``` -------------------------------- ### RequireTotp LiveView Hook Configuration Source: https://github.com/team-alembic/ash_authentication_phoenix/blob/main/documentation/tutorials/totp-2fa.md Use the RequireTotp on_mount hook for LiveView routes to enforce 2FA. Configure options like setup_path and current_user_assign as a tuple. ```elixir defmodule MyAppWeb.Router do use MyAppWeb, :router use AshAuthentication.Phoenix.Router import AshAuthentication.Phoenix.LiveSession.RequireTotp scope "/", MyAppWeb do pipe_through :browser ash_authentication_live_session :protected, on_mount: [{AshAuthentication.Phoenix.LiveSession.RequireTotp, :require_totp}] do live "/dashboard", DashboardLive live "/settings", SettingsLive end end end ``` ```elixir on_mount: [{AshAuthentication.Phoenix.LiveSession.RequireTotp, {:require_totp, setup_path: "/setup-2fa", current_user_assign: :actor }}] ``` -------------------------------- ### Define UI Overrides Module Source: https://github.com/team-alembic/ash_authentication_phoenix/blob/main/documentation/tutorials/ui-overrides.md Create a module that uses `AshAuthentication.Phoenix.Overrides` to define custom properties for UI components. This example shows how to override image URLs for the Banner component. ```elixir defmodule MyAppWeb.AuthOverrides do use AshAuthentication.Phoenix.Overrides # Override a property per component override AshAuthentication.Phoenix.Components.Banner do # include any number of properties you want to override set :image_url, "/images/rickroll.gif" set :dark_image_url, "/images/rickroll-dark.gif" end end ``` -------------------------------- ### Configure Gettext for Internationalization Source: https://context7.com/team-alembic/ash_authentication_phoenix/llms.txt Integrate Gettext for translating authentication UI text. Copy translation templates and ensure the backend is correctly configured in the router. ```elixir # In your router scope "/", MyAppWeb do sign_in_route( overrides: [MyAppWeb.AuthOverrides, AshAuthentication.Phoenix.Overrides.Default], gettext_backend: {MyAppWeb.Gettext, "auth"}, auth_routes_prefix: "/auth" ) end # Copy translation templates from the package: # cp -rv deps/ash_authentication_phoenix/i18n/gettext/* priv/gettext # The package includes translations for multiple languages # including German (de) and Swedish (sv) ``` -------------------------------- ### Configure Confirmation Route Source: https://context7.com/team-alembic/ash_authentication_phoenix/llms.txt Set up the route for email confirmation, used to verify user email addresses. This is typically used when require_interaction? is true. ```elixir defmodule MyAppWeb.Router do use MyAppWeb, :router use AshAuthentication.Phoenix.Router scope "/", MyAppWeb do pipe_through :browser # Confirmation page (when require_interaction? is true) confirm_route MyApp.Accounts.User, :confirm, path: "/confirm", auth_routes_prefix: "/auth" end end ``` -------------------------------- ### Implement Email Delivery Logic Source: https://github.com/team-alembic/ash_authentication_phoenix/blob/main/documentation/tutorials/get-started.md Handles the construction and delivery of password reset emails using Swoosh. ```elixir defmodule Example.Accounts.Emails do @moduledoc """ Delivers emails. """ import Swoosh.Email def deliver_reset_password_instructions(user, url) do if !url do raise "Cannot deliver reset instructions without a url" end deliver(user.email, "Reset Your Password", """

Hi #{user.email},

Click here to reset your password.

If you didn't request this change, please ignore this.

") end # For simplicity, this module simply logs messages to the terminal. # You should replace it by a proper email or notification tool, such as: # # * Swoosh - https://hexdocs.pm/swoosh # * Bamboo - https://hexdocs.pm/bamboo # defp deliver(to, subject, body) do IO.puts("Sending email to #{to} with subject #{subject} and body #{body}") new() |> from({"Zach", "zach@ash-hq.org"}) # TODO: Replace with your email |> to(to_string(to)) |> subject(subject) |> put_provider_option(:track_links, "None") |> html_body(body) |> Example.Mailer.deliver!() end end ``` -------------------------------- ### Configure Router with Overrides and Gettext Source: https://github.com/team-alembic/ash_authentication_phoenix/blob/main/documentation/tutorials/ui-overrides.md Modify your router to include the custom overrides module and specify the Gettext backend for internationalization. Ensure the default overrides are listed last. ```elixir defmodule MyAppWeb.Router do use MyAppWeb, :router use AshAuthentication.Phoenix.Router # ... scope "/", MyAppWeb do sign_in_route overrides: [MyAppWeb.AuthOverrides, AshAuthentication.Phoenix.Overrides.Default], gettext_backend: {MyAppWeb.Gettext, "auth"} end end ``` -------------------------------- ### Implement Role-Based Access Control Source: https://github.com/team-alembic/ash_authentication_phoenix/blob/main/documentation/tutorials/liveview.md Add `on_mount` hooks to check user roles for access control. These functions verify if the current user has the required role before allowing access to a LiveView. ```elixir def on_mount({:required_role, role}, _params, _session, socket) do if socket.assigns[:current_user] && socket.assigns[:current_user].role == role do {:cont, socket} else {:halt, Phoenix.LiveView.redirect(socket, to: ~p"/")} end end ``` ```elixir def on_mount({:required_roles, roles}, _params, _session, socket) do if socket.assigns[:current_user] && Enum.any?(roles, &(socket.assigns[:current_user].role == &1)) do {:cont, socket} else {:halt, Phoenix.LiveView.redirect(socket, to: ~p"/")} end end ``` -------------------------------- ### Update Formatter Configuration Source: https://github.com/team-alembic/ash_authentication_phoenix/blob/main/documentation/tutorials/get-started.md Include ash_authentication_phoenix in the formatter dependencies. ```elixir [ import_deps: [ :ash_authentication_phoenix, # <-------- Add this line :ash_authentication, :ash_postgres, :ash, :ecto, :ecto_sql, :phoenix ], subdirectories: ["priv/*/migrations"], plugins: [Spark.Formatter, Phoenix.LiveView.HTMLFormatter], inputs: ["*.{heex,ex,exs}", "{config,lib,test}/**/*.{heex,ex,exs}", "priv/*/seeds.exs"] ] ``` -------------------------------- ### Displaying Metadata in Templates Source: https://github.com/team-alembic/ash_authentication_phoenix/blob/main/documentation/topics/authentication-metadata.md Render authentication information in a HEEx template. ```heex

Current Session

Authenticated with: <%= Enum.map_join(@auth_strategies, ", ", &to_string/1) %>

<%= if @totp_verified_at do %>

TOTP verified at: <%= Calendar.strftime(@totp_verified_at, "%Y-%m-%d %H:%M UTC") %>

<% end %>
``` -------------------------------- ### Wrap Live Routes with Live Session Source: https://github.com/team-alembic/ash_authentication_phoenix/blob/main/documentation/tutorials/liveview.md Wrap your LiveView routes with `ash_authentication_live_session` to automatically set user assigns. This provides a basic session wrapper. ```elixir ash_authentication_live_session :session_name do live "/route", ProjectLive.Index, :index end ``` -------------------------------- ### Check TOTP Status with TotpHelpers Source: https://github.com/team-alembic/ash_authentication_phoenix/blob/main/documentation/tutorials/totp-2fa.md Use the TotpHelpers module to check if a user has TOTP configured, if a resource supports TOTP, or to retrieve the TOTP strategy. Ensure the `AshAuthentication.Phoenix.TotpHelpers` module is aliased. ```elixir alias AshAuthentication.Phoenix.TotpHelpers # Check if user has TOTP configured TotpHelpers.totp_configured?(user) #=> true # Check if resource supports TOTP TotpHelpers.totp_available?(MyApp.Accounts.User) #=> true # Get the TOTP strategy {:ok, strategy} = TotpHelpers.get_totp_strategy(MyApp.Accounts.User) ``` -------------------------------- ### Configure Magic Link Authentication Route Source: https://context7.com/team-alembic/ash_authentication_phoenix/llms.txt Set up the route for magic link (passwordless) sign-in. This is typically used when require_interaction? is true. ```elixir defmodule MyAppWeb.Router do use MyAppWeb, :router use AshAuthentication.Phoenix.Router scope "/", MyAppWeb do pipe_through :browser # Magic link sign-in page (when require_interaction? is true) magic_sign_in_route MyApp.Accounts.User, :magic_link, path: "/magic-link", auth_routes_prefix: "/auth" end end ``` -------------------------------- ### Logging Authentication Events Source: https://github.com/team-alembic/ash_authentication_phoenix/blob/main/documentation/topics/authentication-metadata.md Create an audit log entry using authentication metadata. ```elixir defmodule MyApp.AuthAudit do def log_authentication(user, conn) do MyApp.AuditLog.create!(%{ user_id: user.id, strategies: user.__metadata__[:authentication_strategies] || [], totp_verified: user.__metadata__[:totp_verified_at] != nil, ip_address: get_ip(conn), user_agent: get_user_agent(conn), timestamp: DateTime.utc_now() }) end defp get_ip(conn) do conn.remote_ip |> :inet.ntoa() |> to_string() end defp get_user_agent(conn) do Plug.Conn.get_req_header(conn, "user-agent") |> List.first() end end ``` -------------------------------- ### Implement Email Notifier Source: https://github.com/team-alembic/ash_authentication_phoenix/blob/main/documentation/tutorials/password-change.md Create a notifier module to trigger email delivery on password change. ```elixir defmodule MyApp.Notifiers.EmailNotifier do use Ash.Notifier alias MyApp.Accounts.User @impl true def notify(%Ash.Notifier.Notification{action: %{name: :change_password}, data: user}) do User.Email.deliver_password_change_notification(user) end end ``` -------------------------------- ### Define LiveUserAuth on_mount Hooks Source: https://github.com/team-alembic/ash_authentication_phoenix/blob/main/documentation/tutorials/liveview.md Implement `on_mount` hooks in `MyAppWeb.LiveUserAuth` for different authentication behaviors: optional user, required user, and no user required. These hooks manage user session state and redirects. ```elixir # lib/my_app_web/live_user_auth.ex defmodule MyAppWeb.LiveUserAuth do @moduledoc """ Helpers for authenticating users in LiveViews. """ import Phoenix.Component use MyAppWeb, :verified_routes def on_mount(:live_user_optional, _params, _session, socket) do if socket.assigns[:current_user] do {:cont, socket} else {:cont, assign(socket, :current_user, nil)} end end def on_mount(:live_user_required, _params, _session, socket) do if socket.assigns[:current_user] do {:cont, socket} else {:halt, Phoenix.LiveView.redirect(socket, to: ~p"/sign-in")} end end def on_mount(:live_no_user, _params, _session, socket) do if socket.assigns[:current_user] do {:halt, Phoenix.LiveView.redirect(socket, to: ~p"/")} else {:cont, assign(socket, :current_user, nil)} end end end ``` -------------------------------- ### Define Authentication Routes with AshAuthentication.Phoenix.Router Source: https://context7.com/team-alembic/ash_authentication_phoenix/llms.txt Use router macros to generate routes for authentication flows. Import `AshAuthentication.Phoenix.Router` in your router file to access these helpers. ```elixir defmodule MyAppWeb.Router do use MyAppWeb, :router use AshAuthentication.Phoenix.Router pipeline :browser do plug :accepts, ["html"] plug :fetch_session plug :fetch_live_flash plug :put_root_layout, {MyAppWeb.Layouts, :root} plug :protect_from_forgery plug :put_secure_browser_headers plug :load_from_session end pipeline :api do plug :accepts, ["json"] plug :load_from_bearer plug :set_actor, :user end scope "/", MyAppWeb do pipe_through :browser # Controller-backed authentication routes auth_routes AuthController, MyApp.Accounts.User, path: "/auth" # Sign out with CSRF protection (GET shows confirmation, DELETE signs out) sign_out_route AuthController # LiveView sign-in page with registration and reset toggles sign_in_route( register_path: "/register", reset_path: "/reset", auth_routes_prefix: "/auth" ) # Password reset page (accessed via token in email) reset_route auth_routes_prefix: "/auth" end end ``` -------------------------------- ### Use DaisyUI Theme Support Source: https://context7.com/team-alembic/ash_authentication_phoenix/llms.txt Apply DaisyUI-specific overrides for styling instead of the default TailwindCSS theme. Configure this in your router. ```elixir # In your router scope "/", MyAppWeb do sign_in_route( register_path: "/register", reset_path: "/reset", auth_routes_prefix: "/auth", overrides: [AshAuthentication.Phoenix.Overrides.DaisyUI] ) end # In your app.css for dark theme support: # @custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *)); ``` -------------------------------- ### Configure Phoenix Router for Ash Authentication Source: https://github.com/team-alembic/ash_authentication_phoenix/blob/main/documentation/tutorials/get-started.md Add authentication routes and plugs to the Phoenix router module. ```elixir defmodule ExampleWeb.Router do use ExampleWeb, :router # add these lines --> use AshAuthentication.Phoenix.Router import AshAuthentication.Plug.Helpers # <-- add these lines pipeline :browser do plug :accepts, ["html"] plug :fetch_session plug :fetch_live_flash plug :put_root_layout, {ExampleWeb.Layouts, :root} plug :protect_from_forgery plug :put_secure_browser_headers plug :load_from_session # <-------- Add this line end pipeline :api do plug :accepts, ["json"] # add these lines --> plug :load_from_bearer plug :set_actor, :user # <-- add these lines end scope "/", ExampleWeb do pipe_through :browser get "/", PageController, :home # add these lines --> # Standard controller-backed routes auth_routes AuthController, Example.Accounts.User, path: "/auth" sign_out_route AuthController # Prebuilt LiveViews for signing in, registration, resetting, etc. # Leave out `register_path` and `reset_path` if you don't want to support # user registration and/or password resets respectively. sign_in_route(register_path: "/register", reset_path: "/reset", auth_routes_prefix: "/auth") reset_route [auth_routes_prefix: "/auth"] # <-- add these lines end ... end ``` -------------------------------- ### OAuth2 Components Source: https://github.com/team-alembic/ash_authentication_phoenix/blob/main/documentation/tutorials/ui-overrides.md Configuration parameters for Apple and general OAuth2 sign-in button components. ```APIDOC ## AshAuthentication.Phoenix.Components.Apple / OAuth2 ### Description Generates sign-in buttons for Apple or generic OAuth2 strategies. ### Parameters - **icon_class** (string) - CSS classes for the icon SVG. - **link_class** (string) - CSS classes for the a element. - **root_class** (string) - CSS classes for the root div element. - **icon_src** (map) - (OAuth2 only) Map of strategy names to icon sources. ``` -------------------------------- ### Accessing Authentication Strategies Source: https://github.com/team-alembic/ash_authentication_phoenix/blob/main/documentation/topics/authentication-metadata.md Retrieve the list of authentication strategies used in the current session from the user metadata. ```elixir user.__metadata__.authentication_strategies #=> [:password] # Or for TOTP #=> [:totp] ``` -------------------------------- ### Preserve Metadata via Session Storage Source: https://github.com/team-alembic/ash_authentication_phoenix/blob/main/documentation/topics/authentication-metadata.md Demonstrates how to extract metadata from the user struct into a Phoenix session and restore it to the user struct using a plug. ```elixir # In your auth controller callback def callback(conn, _params) do user = conn.assigns.subject conn |> put_session(:auth_strategies, user.__metadata__[:authentication_strategies]) |> put_session(:totp_verified_at, user.__metadata__[:totp_verified_at]) |> redirect(to: "/") end # In a plug to restore metadata def restore_auth_metadata(conn, _opts) do if user = conn.assigns[:current_user] do user = user |> Ash.Resource.put_metadata(:authentication_strategies, get_session(conn, :auth_strategies)) |> Ash.Resource.put_metadata(:totp_verified_at, get_session(conn, :totp_verified_at)) assign(conn, :current_user, user) else conn end end ``` -------------------------------- ### Customize Authentication Routes Source: https://github.com/team-alembic/ash_authentication_phoenix/blob/main/documentation/tutorials/get-started.md Specify custom layouts and prefixes for authentication routes using route helper options. ```elixir reset_route(layout: {MyAppWeb, :live}, auth_routes_prefix: "/auth") ``` -------------------------------- ### API Authentication Controller with JSON Responses Source: https://context7.com/team-alembic/ash_authentication_phoenix/llms.txt Implement API authentication callbacks to return JSON responses, including bearer tokens for successful authentication. ```elixir defmodule MyAppWeb.ApiAuthController do use MyAppWeb, :controller use AshAuthentication.Phoenix.Controller def success(conn, _activity, _user, token) do conn |> put_status(200) |> json(%{ authentication: %{ status: :success, bearer: token } }) end def failure(conn, _activity, _reason) do conn |> put_status(401) |> json(%{ authentication: %{ status: :failed } }) end def sign_out(conn, _params) do conn |> revoke_bearer_tokens() |> json(%{status: :ok}) end end ``` -------------------------------- ### Apply Authentication Overrides in Router Source: https://context7.com/team-alembic/ash_authentication_phoenix/llms.txt Apply custom authentication overrides by including your override module in the `overrides` list when defining sign-in routes. This ensures your customizations are used across authentication flows. ```elixir scope "/", MyAppWeb do sign_in_route( overrides: [MyAppWeb.AuthOverrides, AshAuthentication.Phoenix.Overrides.Default], auth_routes_prefix: "/auth" ) end ``` -------------------------------- ### Configure LiveView Hook Source: https://github.com/team-alembic/ash_authentication_phoenix/blob/main/documentation/tutorials/recovery-codes.md Add the RequireRecoveryCodes hook to your live_session to protect LiveView routes. ```elixir live_session :require_recovery_codes, on_mount: [ {AshAuthentication.Phoenix.LiveSession, :default}, {AshAuthentication.Phoenix.LiveSession.RequireRecoveryCodes, :require_recovery_codes} ] do live "/secure", SecureLive end ``` -------------------------------- ### Configure TOTP in User Resource Source: https://github.com/team-alembic/ash_authentication_phoenix/blob/main/documentation/tutorials/totp.md Define the TOTP strategy and required attributes within the Ash resource. ```elixir defmodule MyApp.Accounts.User do use Ash.Resource, extensions: [AshAuthentication], domain: MyApp.Accounts authentication do tokens do enabled? true token_resource MyApp.Accounts.Token signing_secret MyApp.Secrets end strategies do totp do identity_field :email issuer "MyApp" sign_in_enabled? true # Optional: require confirmation before storing secret confirm_setup_enabled? true end end end attributes do uuid_primary_key :id attribute :email, :ci_string, allow_nil?: false attribute :totp_secret, :binary, allow_nil?: true, sensitive?: true attribute :last_totp_at, :datetime, allow_nil?: true, sensitive?: true end identities do identity :unique_email, [:email] end end ``` -------------------------------- ### Magic Link Components Source: https://github.com/team-alembic/ash_authentication_phoenix/blob/main/documentation/tutorials/ui-overrides.md Components for handling magic link sign-in functionality. ```APIDOC ## AshAuthentication.Phoenix.MagicSignInLive ### Description A generic, white-label confirmation page. ### Parameters #### Query Parameters - **:magic_sign_in_id** (string) - Optional - Element ID for the `MagicSignIn` LiveComponent. - **:root_class** (string) - Optional - CSS class for the root `div` element. ``` ```APIDOC ## AshAuthentication.Phoenix.Components.MagicLink ### Description Generates a sign-in form for a resource using the "Magic link" strategy. ### Parameters #### Query Parameters - **:checkbox_class** (string) - Optional - CSS class for the `remember_me` checkbox. - **:checkbox_label_class** (string) - Optional - CSS class for the `remember_me` check box label. - **:disable_button_text** (string) - Optional - Text for the submit button when the request is happening. - **:form_class** (string) - Optional - CSS class for the `form` element. - **:label_class** (string) - Optional - CSS class for the `h2` element. - **:remember_me_class** (string) - Optional - CSS class for the `div` element surrounding the remember me field. - **:request_flash_text** (string) - Optional - Text for the flash message when a request is received. Set to `nil` to disable. - **:root_class** (string) - Optional - CSS class for the root `div` element. ``` ```APIDOC ## AshAuthentication.Phoenix.Components.MagicLink.SignIn ### Description Renders a magic sign in button. ### Parameters #### Query Parameters - **:root_class** (string) - Optional - CSS class for the root `div` element. - **:show_banner** (boolean) - Optional - Whether or not to show the banner. - **:strategy_class** (string) - Optional - CSS class for the `div` surrounding each strategy component. ``` ```APIDOC ## AshAuthentication.Phoenix.Components.MagicLink.Form ### Description Generates a default magic sign in form. ### Parameters #### Query Parameters - **:disable_button_text** (string) - Optional - Text for the submit button when the request is happening. - **:form_class** (string) - Optional - CSS class for the `form` element. - **:label_class** (string) - Optional - CSS class for the `h2` element. - **:root_class** (string) - Optional - CSS class for root `div` element. ```