How to use current_user in Elixir's Phoenix Framework

How to use current_user in Elixir's Phoenix Framework

At Remote.com we’re heavy users of Phoenix Framework. As we make good progress to ship the new Remote.com there are a few tips and tricks we’re happy to share.

We’re using coherence as the backbone for our auth system. It’s quite easy and fast to setup.

Soon after you finish all requirements and setup procedures it’ll be time to get going and actually implement some auth flows.

The Current User

If you already implemented your fair share of web systems, I’m sure the term and/or variable current_user is one you know all too well.

If that isn’t the case, well, in short, it’s typically a variable populated with a data structure or record identifying the user that originated the respective request.

current_user

A very common way to access this variable without having to fetch it explicitely from conn in every action of our controller is to override the action plug.

As a first attempt to simplify that, you can do the following:

defmodule MyApp.UserController do
  use MyApp.Web, :controller

  def action(%Plug.Conn{assigns: %{current_user: current_user}} = conn, _opts) do
    apply(__MODULE__, action_name(conn), [conn, conn.params, current_user])
  end

  def show(conn, _params, current_user) do
    render(conn, "show.html", logged_in: current_user)
  end

As you can see we now have a function called action which will override Phoenix’s controller default action/2 plug allowing us to inject custom arguments. More about this here.

Although this is a lot better, it’s still not very DRY as we’d need to replicate this function all over every single controller in which we need to access current_user.

We improved this by creating the following module in controllers/helpers/current_user.ex:

defmodule MyApp.CurrentUser do
  defmacro __using__(_) do
    quote do
      def action(%Plug.Conn{assigns: %{current_user: current_user}} = conn, _opts) do
        apply(__MODULE__, action_name(conn), [conn, conn.params, current_user])
      end
    end
  end
end

This time we applied Elixir’s defmacro to allow us to seamlessly inject the function into our controllers. With this we can access current_user very easily and remove unnecessary, repeated code, like this:

defmodule MyApp.UserController do
  use MyApp.Web, :controller
  use MyApp.CurrentUser

  def show(conn, _params, current_user) do
    render(conn, "show.html", logged_in: current_user)
  end

How cool is that? With a simple, single line of code we can inject current_user into every action.

Do you have any suggestions on how to improve this? We’d love to hear from you.