EzAuth

EzAuth is an opinionated and batteries-included auth library inspired by PowAuth, Ueberauth, Clerk, BetterAuth and Supabase Auth.

Key Features

Installation

To install EzAuth, add it to your dependencies in mix.exs:

def deps do
  [
    {:ez_auth, "~> 0.1.0"}
  ]
end

Step 1 - Run the generator

Run the installer from your Phoenix application:

mix ez_auth.install

Then apply the generated migrations to your DB with mix ecto.migrate.

NOTE: EzAuth generates migrations assuming PostgreSQL features by default (such as citext). You are free to adjust it to your needs as long as you don't drastically modify the core schema the library relies on.

Step 2 - Add basic configuration

Start with a simple password authentication strategy:

config :ez_auth,
  repo: MyApp.Repo,
  after_sign_in_path: "/",
  sign_in_path: "/sign-in",
  endpoint: MyAppWeb.Endpoint,
  gettext_backend: MyAppWeb.Gettext,
  strategies: [EzAuth.Strategies.Password]

Step 3 - Update your router

Add use EzAuth to your Router so the helpers are imported:

 defmodule MyAppWeb.Router do
   use MyAppWeb, :router
+  use EzAuth

Add fetch_current_scope to the browser pipeline:

 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 :fetch_current_scope
 end

Protect your routes:

scope "/" do
  pipe_through :browser

  auth_routes()
end

 scope "/", MyAppWeb do
   pipe_through :browser

   live "/sign-in", SignInLive
   live "/sign-up", SignUpLive
 end

scope "/", MyAppWeb do
  pipe_through [:browser, :require_authenticated]

  get "/settings", SettingsController, :edit

  live_session :authenticated, on_mount: [{EzAuth, :require_authenticated}] do
    # All of your protected LiveView pages should go here :)
  end
end

Do not place the auth_routes macro inside nested scopes, otherwise EzAuth's internal routes won't be properly picked up. Routes are hardcoded to overcome Phoenix's 1.8 new verified routes limitations requiring additional boilerplate code to make this work.

Step 4 - Render the UI components

defmodule MyAppWeb.SignInLive do
  use MyAppWeb, :live_view

  def render(assigns) do
    ~H"""
    <.live_component module={EzAuth.UI.SignIn} id="sign-in" />
    """
  end
end
defmodule MyAppWeb.SignUpLive do
  use MyAppWeb, :live_view

  def render(assigns) do
    ~H"""
    <.live_component module={EzAuth.UI.SignUp} id="sign-up" />
    """
  end
end

Custom behavior

Add these only when your app wants to customize message delivery or the HTTP response after EzAuth actions.

Sender

To deliver verification or recovery messages yourself, configure a sender:

 config :ez_auth,
+  sender: MyApp.AuthSender

For example, the :email event is emitted when EzAuth creates an email verification token:

defmodule MyApp.AuthSender do
  @behaviour EzAuth.Sender

  alias EzAuth.Scopes.SenderScope

  @impl true
  def deliver(:email, scope) do
    confirmation_url = SenderScope.password_confirmation(scope)

    # Deliver confirmation_url to scope.user
  end
end

Handler

To customize success or failure responses, configure a handler:

scope "/" do
  pipe_through :browser
  auth_routes(handler: MyAppWeb.AuthHandler)
end

You can then handle success and failure requests like this:

defmodule MyAppWeb.AuthHandler do
  @behaviour EzAuth.Handler

  import Phoenix.Controller

  @impl true
  def handle_success(conn, {:default, :sign_up}, _user) do
    conn
    |> put_flash(:info, "Check your email to confirm your account.")
    |> redirect(to: EzAuth.Config.sign_in_path())
  end

  @impl true
  def handle_failure(conn, {:password, :request}, _reason) do
    conn
    |> put_flash(:error, "Invalid email or password.")
    |> redirect(to: EzAuth.Config.sign_in_path())
  end

  @impl true
  def handle_success(conn, {:password, :callback}, _user) do
    conn
    |> put_flash(:info, "Your email has been confirmed. You can sign in now.")
    |> redirect(to: EzAuth.Config.sign_in_path())
  end
end

Components & Styling

EzAuth provides ready-to-use components such as EzAuth.UI.SignIn and EzAuth.UI.SignUp for the standard auth experience. If you need a custom flow, check the base components from EzAuth.UI.Core and EzAuth.UI. Additionally, you can build your own components with your own logic and simply use the helpers we provide.

EzAuth components are intentionally unstyled so they can adapt to any application. You add your own styles by targeting each component's data-part attributes. The default Storybook stylesheet in storybook/static/storybook.css is a good starting point if you want to see one complete styling approach. To see every available component and flow, run Storybook: mix storybook.