README.md

# EzAuth

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

## Key Features

- **⚡ Plug and Play**: Integrates easily with Phoenix so you can get auth up and running in no time.
- **🔋 Batteries Included**: Add a complete auth experience without piecing together every flow by hand.
- **🔐 Flexible Authentication**: Start simple and grow into the sign-in experience your product needs.
- **🎨 Customization**: Ship with pre-built components, then shape the experience to match your UI/UX.

## Installation

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


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

### Step 1 - Run the generator

Run the installer from your Phoenix application:

```bash
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:

```elixir
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:

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

Add `fetch_current_scope` to the browser pipeline:

```diff
 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:

```elixir
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

```elixir
defmodule MyAppWeb.SignInLive do
  use MyAppWeb, :live_view

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

```elixir
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:

```diff
 config :ez_auth,
+  sender: MyApp.AuthSender
```

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

```elixir
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:

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

You can then handle success and failure requests like this:

```elixir
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`](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`.