Skip to content
This repository has been archived by the owner on Jan 19, 2022. It is now read-only.

Authorization

David Whitlock edited this page Apr 2, 2017 · 5 revisions

This page looks at how to handle access control using the output from Openmaize.Authenticate.

The first section deals with creating a Plug which you can add to the controller, and the second section looks at overriding the default action function (this method can only be used with Phoenix apps).

The functions user_check, id_check, auth_error and auth_action in the examples below are all defined in this module.

Authorization using Plug

Checking for a current user

The most basic check is the user_check function, which just checks if the current user is not nil. To use this function, add the following line to your controller:

plug :user_check

the above line calls the user_check function before every route in the controller. You can also specify which routes you want it run before:

plug :user_check when action in [:index, :show]

or which routes you do not want it run before:

plug :user_check when not action in [:new, :create]

Checking the current user id

The id_check function checks that the id of the current user is the same as the id in the route. It can be used in the same way as user_check:

plug :id_check when action in [:edit, :update, :delete]

Authorization based on user role

The following Plug can be used to grant / deny access based on user role:

def role_check(%Plug.Conn{assigns: %{current_user: nil}} = conn, _opts) do
  auth_error conn, "You need to log in to view this page", session_path(conn, :new)
end
def role_check(%Plug.Conn{assigns: %{current_user: current_user}} = conn, opts) do
  if opts[:roles] && current_user.role in opts[:roles], do: conn,
  else: auth_error conn, "You are not authorized to view this page", user_path(conn, :index)
end

To call this Plug, add the following line to the controller:

plug :role_check, [roles: ["admin", "user"]] when action in [:create, :update]

More complex examples

The following Plug shows how you can combine id_check and role_check:

def id_or_admin(%Plug.Conn{assigns: %{current_user: nil}} = conn, _opts) do
  auth_error conn, "You need to log in to view this page", session_path(conn, :new)
end
def id_or_admin(%Plug.Conn{params: %{"id" => id}, assigns: %{current_user:
 %{id: current_id}}} = conn, _opts) do
  if id == to_string(current_id) or current_user.role == "admin", do: conn,
  else: auth_error conn, "You are not authorized to view this page", user_path(conn, :index)
end

In this example, after the check for user id, there is an additional check to see if the current_user is an admin. In other words, this resource is accessable to a normal user if it is his / her own page and any admin.

Overriding the action function

In Phoenix apps, the function action/2 is called before each route. We can override this and authorize the user at this stage. This will affect every route in the controller.

In all the examples below, we will add the current user to the output. As a result, you will need to add user to the argument list in each function. For example, def index(conn, params) will become def index(conn, params, user).

Checking for a current user

The auth_action function in the example Authorize module just checks for a current user. It then either sends the arguments conn, params, current_user to the route or denies access to the user.

To use this function, add the following line to the controller:

def action(conn, _), do: auth_action(conn, __MODULE__)

Checking the current user id

In this case, the action function is very similar to the id_check function:

def auth_id(%Plug.Conn{assigns: %{current_user: nil}} = conn, _) do
  auth_error conn, "You need to log in to view this page", session_path(conn, :new)
end
def auth_id(%Plug.Conn{assigns: %{current_user: %{id: current_id}},
  params: %{"id" => id} = params} = conn, module) do
  if id == to_string(current_id) do
    apply(module, action_name(conn), [conn, params, current_user])
  else
    auth_error conn, "You are not authorized to view this page", user_path(conn, :index)
  end
end

Call the function in the same way, by adding the following line to the controller:

def action(conn, _), do: auth_id(conn, __MODULE__)

Authorization based on user role

The following action function can be used to grant / deny access based on user role:

def auth_role(%Plug.Conn{assigns: %{current_user: nil}} = conn, _, _) do
  auth_error conn, "You need to log in to view this page", session_path(conn, :new)
end
def auth_role(%Plug.Conn{assigns: %{current_user: current_user}} = conn, roles, module) do
  if current_user.role in roles do
    apply(module, action_name(conn), [conn, conn.params, current_user])
  else
    auth_error conn, "You are not authorized to view this page", user_path(conn, :index)
  end
end

In this case, call the function with the list of permitted roles as well as the module name:

def action(conn, _), do: auth_role(conn, ["admin", "user"], __MODULE__)