# `KeenAuth.EmailAuthenticationHandler`
[🔗](https://github.com/KeenMate/keen_auth/blob/main/lib/email_authentication_handler.ex#L1)

Behaviour for implementing email/password authentication.

This is the entry point for email-based authentication, equivalent to what
OAuth providers (via Assent) do for OAuth flows. Your implementation verifies
credentials and returns a raw user map that flows through the KeenAuth pipeline.

## Pipeline Position

```
┌──────────────────────┐
│ EmailAuthHandler     │  ◀── YOU IMPLEMENT THIS
│ (credential verify)  │
└──────────┬───────────┘
           │ {:ok, raw_user}
           ▼
┌──────────────────────┐
│ Mapper               │  Normalize to KeenAuth.User
└──────────┬───────────┘
           ▼
┌──────────────────────┐
│ Processor            │  Business logic, DB ops
└──────────┬───────────┘
           ▼
┌──────────────────────┐
│ Storage              │  Persist session
└──────────────────────┘
```

## Callbacks

- `authenticate/2` - Verify credentials, return `{:ok, user_map}` or `{:error, reason}`
- `handle_authenticated/2` - Called after successful auth (logging, side effects)
- `handle_unauthenticated/3` - Handle failed auth (render error, redirect)

## Example Implementation

    defmodule MyApp.Auth.EmailHandler do
      @behaviour KeenAuth.EmailAuthenticationHandler
      import Plug.Conn
      import Phoenix.Controller

      @impl true
      def authenticate(_conn, %{"email" => email, "password" => password}) do
        with {:ok, user} <- MyApp.Accounts.get_user_by_email(email),
             :ok <- MyApp.Accounts.verify_password(user, password) do
          # Return raw user map - will be passed to Mapper
          {:ok, %{
            "sub" => to_string(user.id),
            "email" => user.email,
            "name" => user.display_name,
            "preferred_username" => user.username
          }}
        else
          _ -> {:error, :invalid_credentials}
        end
      end

      @impl true
      def handle_authenticated(conn, user) do
        # Optional: audit log, analytics, etc.
        Logger.info("User #{user.email} signed in via email")
        conn
      end

      @impl true
      def handle_unauthenticated(conn, params, {:error, :invalid_credentials}) do
        conn
        |> put_flash(:error, "Invalid email or password")
        |> redirect(to: params["redirect_to"] || "/login")
      end

      def handle_unauthenticated(conn, _params, _other_error) do
        conn
        |> put_flash(:error, "Authentication failed")
        |> redirect(to: "/login")
      end
    end

## Configuration

    config :keen_auth,
      email_enabled: true,
      strategies: [
        email: [
          authentication_handler: MyApp.Auth.EmailHandler,
          mapper: KeenAuth.Mapper.Default,
          processor: MyApp.Auth.Processor
        ]
      ]

# `authenticate`

```elixir
@callback authenticate(conn :: Plug.Conn.t(), params :: map()) ::
  {:ok, map()} | {:error, term()}
```

Verify user credentials and return raw user data.

Called when a user submits the login form. Should validate the email/password
and return a map with user data that will be passed to the Mapper.

## Return Values

- `{:ok, user_map}` - Credentials valid. `user_map` should include keys like
  `"sub"`, `"email"`, `"name"` that your Mapper expects.
- `{:error, reason}` - Credentials invalid. `reason` is passed to
  `handle_unauthenticated/3`.

# `handle_authenticated`

```elixir
@callback handle_authenticated(conn :: Plug.Conn.t(), user :: KeenAuth.User.t()) ::
  Plug.Conn.t()
```

Called after successful authentication, before redirect.

Use for side effects like audit logging. The return value should be the conn
(potentially modified).

# `handle_unauthenticated`

```elixir
@callback handle_unauthenticated(
  conn :: Plug.Conn.t(),
  params :: map(),
  error :: term()
) :: Plug.Conn.t()
```

Handle failed authentication attempts.

Called when `authenticate/2` returns an error. Should render an error message
or redirect the user back to the login page.

The controller adds a random delay before calling this to prevent timing attacks.

# `authenticate`

# `current_authentication_handler!`

# `get_authentication_handler!`

# `handle_authenticated`

# `handle_unauthenticated`

---

*Consult [api-reference.md](api-reference.md) for complete listing*
