As I’m mentioned before I have an ongoing project being developed using Elixir and Phoenix. I’m using two third party packages that I wanted to write about in this post. They are Guardian and Canary.

User Authentication and Authorization

In my app I’m using Guardian for for authentication and session management. Guardian has a lot of interesting functionality but at the moment I’m using it for simple password based authentication of users. And, for authorization I’m using the Canary library.

If you aren’t familiar with these two terms I’ll explain.

I use authentication to manage logins and sessions. The user authenicates him or herself by providing a user name and password. Once authenticated a session is created on the user’s behalf.

Once logged in the user has a certain set of privileges. For example, a user may be allowed to modify certain resources that he or she owns. But that same user may not be allowed to modify resources that belong to other users. Canary checks to see if a user is authorized to cary out the actions that he or she requests.

Guardian Setup

My Guardian configuration is pretty simple. I wrote up these pipelines for my router for Guardian:

pipeline :browser_session do plug Guardian . Plug . VerifySession plug Guardian . Plug . LoadResource end pipeline :require_login do plug Guardian . Plug . EnsureAuthenticated , handler: MyApp . GuardianErrorHandler end

Then I have created scopes that either support a logged in user or require a logged in user. For example:

scope " /" , MyApp do pipe_through [ :browser , :browser_session ] get " /" , PageController , :index resources " /search" , SearchController , only: [ :index , :show ] end

This scope user the :browser_session pipeine to load a session if one exists. But it doesn’t require a session. This way both logged in users or guest users can browse the home page and search.

But the resouces in this scope require a user to be logged in:

scope " /" , MyApp do pipe_through [ :browser , :browser_session , :require_login ] resources " /rentals" , RentalController do resources " /images" , ImageController , only: [ :create ] end end

Here, I add the :require_login pipeline which uses Guardian’s EnsureAuthenticated plug to require a session. Errors are passed off to my MyApp.GuardianErrorHandler module which is responsible for redirecting guest users to /login . This is a simple module that looks like this:

defmodule MyApp . GuardianErrorHandler do import MyApp . Router . Helpers def unauthenticated ( conn , _params ) do conn |> Phoenix . Controller . put_flash ( :error , " You must be logged in to access that page." ) |> Phoenix . Controller . redirect ( to: login_path ( conn , :new )) end end

My Canary Setup

I use canary in my controllers to check if a user has authorization to view or modify a given resource.

For example, above we saw routes that edit rentals and create images for those rentals. And that these routes require a logged in user. Well, we need more than just a logged in user. The user needs to own those resources.

Here’s an example of how I use Canary to authorize for Images:

defmodule MyApp . RentalController do use MyApp . Web , :controller alias MyApp . Rental plug :scrub_params , " rental" when action in [ :create , :update ] plug :authorize_resource , model: Rental # Actions follow ... end

Canary’s :authorize_resource plug checks if the current user is allowed to act on the a Rental . This of course depends on a can? function. Here’s part of the can? function I’ve written:

def can? ( user , action , Rental ) when action in [ :new , :create ] do user . role == " owner" end def can? ( user , action , rental = % Rental {}) do rental . owner_id == user . id end

Here I decide if the user is authorized depending on the action.

Using Canary with Guardian

All of the above seems pretty great, but it doesn’t work!

One of the problems I had making Guardian and Canary play well together was that Canary depends on being able to find the current user in the connection. Guardian maintains the current user and it can be accessed by calling Guardian.Plug.current_resource(conn) . But, Canary expects the current user in the :current_user assign in the conn.

To make this work I wrote up another small plug:

defmodule MyApp . Plug . CurrentUser do def init ( opts ), do : opts def call ( conn , _opts ) do current_user = Guardian . Plug . current_resource ( conn ) Plug . Conn . assign ( conn , :current_user , current_user ) end end

This plug simply queries the current user from Guardian and put’s it into the :current_user assign in the conn . This copies the data from Guardian’s location to the location Canary expects.

I add this plug to my :require_login pipeline

pipeline :require_login do plug Guardian . Plug . EnsureAuthenticated , handler: MyApp . GuardianErrorHandler plug MyApp . Plug . CurrentUser end

With this plug Canary can access :current_user as an assign and all is well. I also use the :current_user assign in my own code. My controllers don’t need to know about Guardian.

Next Steps

This post focused on how I use Guardian and Canary together to authenticate and authorize in my app. I covered all of my Guardian usage and what I had to do in order to use the two libraries together. But, there’s a bit more to my Canary usage that I hope to write about next week.