Engineering 2 min

How to use current_user in Elixir's Phoenix Framework

Written by Marcelo Lebre
April 3, 2019
Marcelo Lebre

Share

share to linkedInshare to Twittershare to Facebook
Link copied
to clipboard

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.

Jump straight to a key chapter

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:

elixir
1defmodule MyApp.UserController do
2 use MyApp.Web, :controller
3
4 def action(%Plug.Conn{assigns: %{current_user: current_user}} = conn, _opts) do
5 apply(__MODULE__, action_name(conn), [conn, conn.params, current_user])
6 end
7
8 def show(conn, _params, current_user) do
9 render(conn, "show.html", logged_in: current_user)
10 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:

elixir
1defmodule MyApp.CurrentUser do
2 defmacro __using__(_) do
3 quote do
4 def action(%Plug.Conn{assigns: %{current_user: current_user}} = conn, _opts) do
5 apply(__MODULE__, action_name(conn), [conn, conn.params, current_user])
6 end
7 end
8 end
9end

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:

elixir
1defmodule MyApp.UserController do
2 use MyApp.Web, :controller
3 use MyApp.CurrentUser
4
5 def show(conn, _params, current_user) do
6 render(conn, "show.html", logged_in: current_user)
7 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.

Subscribe to receive the latest
Remote blog posts and updates in your inbox.