defmodule AuthN.AuthenticationPlugMixin do
@moduledoc ~S"""
Allows to create a plug enforcing authentication for private routes.
A user-defined module must `use` this module, making the user module a plug, and
implement the `handle_authentication_error/2` behaviour. The
`handle_authentication_error/2` callback receives a `Plug.Conn` struct and an
atom identifying the set of routes that require authentication, and must return
a `Plug.Conn` struct.
Example:
```
defmodule MyAppWeb.Plugs.EnsureAuthenticated do
use AuthN.AuthenticationPlugMixin
import Plug.Conn
import Phoenix.Controller
def handle_authentication_error(conn, :admin_routes),
do: conn |> put_status(401) |> text("unauthenticated") |> halt()
end
```
`EnsureAuthenticated` is now a plug which can be used in the router:
```
pipeline :ensure_admin_routes_authorized do
plug MyAppWeb.Plugs.EnsureAuthenticated,
resource: :admin_routes
end
scope "/admin", MyAppWeb, as: :admin do
pipe_through [:browser, :ensure_admin_routes_authorized]
# code
end
```
"""
@callback handle_authentication_error(Plug.Conn.t(), atom) :: Plug.Conn.t()
defmacro __using__(_args) do
this_module = __MODULE__
quote do
@behaviour unquote(this_module)
def init(opts), do: opts
def call(conn, opts) do
if conn.assigns[:current_user] do
conn
else
__MODULE__.handle_authentication_error(conn, Keyword.get(opts, :resource))
end
end
end
end
end