Authenticating Users using a Token with Phoenix

Phoenix Cookbook : part 2 of 4 published on May 31, 2015

Phoenix is a web framework built using Elixir. One of the great ways to use Phoenix is by building an API (or migrating an existing API like from a Rails app).

With Phoenix, we can use a Plug to authenticate a user using token. For more information about Plugs, see Phoenix’s documentation.

The short version is that a Plug is Phoenix’s version of middleware.

A plug in its most basic format has two functions: init/1 and call/2.

For our init/1 we will just return the options passed in.

In our call/2 we will use the first parameter conn to check for our token, validate the token by finding the user, and then assigning the user to conn.

# /web/plugs/api/authenticate.ex
defmodule YourApp.Api.Plugs.Authenticate do
  import Plug.Conn
  import Ecto.Query

  def init(options) do
    options
  end

  def call(conn, _) do
    result = validate_token(conn.params["token"])
    case result do
      :missing_token -> 
        conn |> send_resp(401, "Missing valid API token") |> halt
      :invalid_token ->
        conn |> send_resp(401, "Invalid API token") |> halt
      { :authenticated, user } ->
        conn |> assign(:current_user, user)
    end
  end

  def validate_token(token) do
    case token do
      nil -> :missing_token
      _ -> assign_user(token)
    end
  end

  def assign_user(token) do
    user = YourApp.User 
      |> where([u], u.authentication_token == ^token)
      |> YourApp.Repo.one
    
    case user do
      nil -> :invalid_token
      _ -> { :authenticated, user }
    end
  end

end

We can use this Plug directly in our controllers by doing:

defmodule YourApp.Api.SomeController do
  use YourApp.Web, :controller

  plug YourApp.Api.Plugs.Authenticate

  ...

end

Our you can integrate it with your :api pipeline in your router.ex by doing:

defmodule YourApp.Router do
  use YourApp.Web, :router
  
  pipeline :api do
    plug :accepts, ["json"]
    plug YourApp.Api.Plugs.Authenticate
  end

  ...

Inside your controller actions, you can access the current user you assigned in your plug by doing:

def show(conn, %{"id" => id}) do
  current_user = conn.assigns[:current_user]
  ...
end

I hope this helps my fellow Phoenix developers.

Next: Exq: The background job solution for Phoenix