Magic-Link Auth Pages

Copy Markdown View Source

StarView ships an optional generator for AshAuthentication magic-link sign-in pages rendered with StarView instead of the default AshAuthentication.Phoenix LiveViews:

mix star_view.setup.auth

This task was included in v0.3.18 and remains available in v0.4.0. It uses the optional Igniter dependency; it generates application code rather than adding an authentication service to the SDK.

What it generates

  • MyAppWeb.AuthController — the /auth/sign-in page. Renders an email form, requests a magic link over a StarView event (no page reload), and swaps to a "check your email" state. Also implements the AshAuthentication.Phoenix.Controller callbacks (success/4, failure/3, sign_out/2).
  • MyAppWeb.Auth.MagicSignIn — the /magic_link/:token landing page. Verifies the token on GET and renders a confirm button; the button POSTs back to exchange the token for a session, so the sign-in happens on a real form submission and the session cookie can be set with a normal redirect.

Router changes

The task adds the routes to the "/" scope through the :browser pipeline:

get "/auth/sign-in", AuthController, :mount
get "/magic_link/:token", Auth.MagicSignIn, :mount
post "/magic_link/:token", Auth.MagicSignIn, :submit

If the router already contains the default AshAuthentication sign-in routes, they are removed and replaced by the generated pages:

  • sign_in_route ... — removed
  • magic_sign_in_route ... — removed
  • sign_out_route AuthController — added when the router uses AshAuthentication.Phoenix.Router and no sign-out route exists yet

The StarView dispatch route (post "/ds/:module/:event") and StarView.Plug.RenameCsrfParam are added if they are missing, so the sign-in form's events work without running the full installer first.

Layout

Both pages render through Layout.auth/1, a full-height auth variant of the generated StarView layout:

<Layout.auth conn={@conn}>
  ...
</Layout.auth>

If your layout module was generated by an older StarView version without auth/1, the task patches it in: it inserts the auth/1 component after app/1 and adds :theme, :html_class, and :body_class attrs to the private root/1 component. See the layout guide for details.

Requirements

The generated pages expect an AshAuthentication magic-link strategy on your user resource with interaction required, so tokens are never consumed by email link scanners:

magic_link do
  identity_field :email
  registration_enabled? true
  require_interaction? true
  sender MyApp.Accounts.User.Senders.SendMagicLinkEmail
end

They also expect these code interfaces on your accounts domain:

resource User do
  define :request_magic_link, action: :request_magic_link, args: [:email]
  define :sign_in_with_magic_link, action: :sign_in_with_magic_link, args: [:token]
end

Both actions are guarded by the AshAuthenticationInteraction policy bypass, which is why the generated code calls them with context: %{private: %{ash_authentication?: true}}.

Finally, point the magic-link sender email at the generated landing page:

url(~p"/magic_link/#{@token}")

Privacy

The sign-in page always reports success after a magic-link request, regardless of whether the account exists, so it does not leak which emails are registered.