Packages

Provides the glue for authenticating HTTP requests.

Current section

Files

Jump to
authenticator lib authenticator.ex
Raw

lib/authenticator.ex

defmodule Authenticator do
@type resource :: any()
@type token :: String.t()
@type reason :: atom()
@callback tokenize(resource) :: {:ok, token} | {:error, reason}
@callback authenticate(token) :: {:ok, resource} | {:error, reason}
@callback fallback(conn :: Plug.Conn.t(), reason) :: Plug.Conn.t()
defmacro __using__(config) do
quote location: :keep do
@behaviour Authenticator
@scope Keyword.get(unquote(config), :scope, :current_user)
@doc """
Attempts to authenticate using the given token. If authentication is
successful, the `conn.assigns.#{@scope}` will be assigned.
If `authenticate/1` fails with `{:error, reason}`, the #{inspect(@scope)}
will be signed out and `fallback/2` will be invoked.
"""
@spec call(Plug.Conn.t(), Authenticator.token()) :: Plug.Conn.t()
def call(conn, token) do
case authenticate(token) do
{:ok, resource} ->
assign(conn, resource)
{:error, reason} ->
conn
|> sign_out()
|> fallback(reason)
end
end
@doc """
Stores the `#{inspect(@scope)}` in the session and sets `conn.assigns.#{@scope}`.
If `tokenize/1` fails with `{:error, reason}`, the #{inspect(@scope)} will be
signed out and `fallback/2` will be invoked.
"""
@spec sign_in(Plug.Conn.t(), Authenticator.resource()) :: Plug.Conn.t()
def sign_in(%Plug.Conn{} = conn, resource) do
case tokenize(resource) do
{:ok, token} ->
conn
|> assign(resource)
|> put_session(token)
{:error, reason} ->
conn
|> sign_out()
|> fallback(reason)
end
end
@doc """
Deletes the `#{inspect(@scope)}` from the session and sets`conn.assigns.#{@scope}`
to `nil`.
"""
@spec sign_out(Plug.Conn.t()) :: Plug.Conn.t()
def sign_out(%Plug.Conn{} = conn) do
conn
|> assign(nil)
|> delete_session()
end
@doc """
Check to see if there is a #{inspect(@scope)} signed in.
"""
@spec signed_in?(Plug.Conn.t()) :: boolean()
def signed_in?(%Plug.Conn{} = conn) do
not is_nil(conn.assigns[@scope])
end
@doc """
Gets the token from the session.
"""
@spec get_session(Plug.Conn.t()) :: any()
def get_session(conn) do
if session_configured?(conn) do
Plug.Conn.get_session(conn, @scope)
end
end
@doc """
Deletes the token from the session.
"""
@spec delete_session(Plug.Conn.t()) :: Plug.Conn.t()
def delete_session(conn) do
if session_configured?(conn) do
Plug.Conn.delete_session(conn, @scope)
else
conn
end
end
@doc """
Sets the token in the session.
"""
@spec put_session(Plug.Conn.t(), any()) :: Plug.Conn.t()
def put_session(conn, token) do
if session_configured?(conn) do
Plug.Conn.put_session(conn, @scope, token)
else
conn
end
end
@doc """
Make sure `conn.assigns.#{@scope}` has been set.
"""
@spec assign(Plug.Conn.t()) :: Plug.Conn.t()
def assign(conn) do
assign(conn, conn.assigns[@scope])
end
@doc """
Set the vaue of `conn.assigns.#{@scope}`.
"""
@spec assign(Plug.Conn.t(), Authenticator.resource()) :: Plug.Conn.t()
def assign(conn, resource) do
Plug.Conn.assign(conn, @scope, resource)
end
defp session_configured?(conn) do
Map.has_key?(conn.private, :plug_session)
end
end
end
end