View Source Client request authorization

Once your authorization server setup done, you can deliver tokens that help limiting access to HTTP services. In order to do so, you can check validity and security information of access tokens provided in requests. Here we will see how restrict access using a bearer token as described in RFC.

In a monolithic application

In a monolith, you have access to Boruta API (documented here) and can directly use it in order to restrict access to endpoints. Creating a Plug and add it to the request pipeline would be the preferred way to perform authorization. Here is an example of basic plugs.

def MyAppWeb.Plugs.Authorization do
  import Plug

  use MyAppWeb, :controller

  alias Boruta.Oauth.Authorization
  alias Boruta.Oauth.Scope

  def require_authenticated(conn, _opts) do
    with [authorization_header] <- get_req_header(conn, "authorization"),
         [_authorization_header, bearer] <- Regex.run(~r/[B|b]earer (.+)/, authorization_header),
         {:ok, token} <- Authorization.AccessToken.authorize(value: bearer) do
      conn
      |> assign(:current_token, token)
      |> assign(:current_user, Accounts.get_user!(token.sub))
    else
      _ ->
        conn
        |> put_status(:unauthorized)
        |> put_view(MyAppWeb.ErrorView)
        |> render("401.json")
        |> halt()
    end
  end

  def authorize(conn, [_h | _t] = required_scopes) do
    current_scopes = Scope.split(conn.assigns[:current_token].scope)

    case Enum.empty?(required_scopes -- current_scopes) do
      true ->
        conn

      false ->
        conn
        |> put_status(:forbidden)
        |> put_view(MyAppWeb.ErrorView)
        |> render("403.json")
        |> halt()
    end
  end
end

Then you can invoke those plugs in your router and controllers:

# lib/my_app_web/router.ex
...
  import MyAppWeb.Plugs.Authorization,
    only: [
      require_authenticated: 2
    ]

  pipeline :protected_api do
    plug(:accepts, ["json"])

    plug(:require_authenticated)
  end
...

# in controllers
...
  import MyAppWeb.Plugs.Authorization,
    only: [
      authorize: 2
    ]

  plug(:authorize, ["resource:read"]) when action in [:index, :show]
  plug(:authorize, ["resource:write"]) when action in [:create, :update, :delete]
...

In a microservice environment

With an authorization server set up, an introspect endpoint is exposed to check token validity and provide security information as described in RFC. You can create your own plugs as above but instead of using Boruta API, request the authorization server to get an introspected token and all information needed to perform authorization.

Note: the grant type introspect must be active on the client you are performing the requests with.

Example of introspect response:

{
    "active": true,
    "client_id": "6a2f41a3-c54c-fce8-32d2-0324e1c32e20",
    "exp": 1639752235,
    "iat": 1639748635,
    "iss": "https://oauth.boruta.patatoid.fr",
    "scope": "resource:read resource:write",
    "sub": "b69c4bb6-a47b-4254-9d87-cf42bb223262",
    "username": "test@test.test"
}