pipeline status coverage report

Boruta OAuth provider core

Boruta is the core of an OAuth provider giving business logic of authentication and authorization.

It is intended to follow RFCs:

As it, it helps implement a provider for authorization code, implicit, client credentials and resource owner password credentials grants. Then it follows Introspection to check tokens.

Documentation

Documentation can be found here

Live example

A live example can be found here

Setup

  1. Schemas migration

If you plan to use Boruta builtin clients and tokens contexts, you'll need a migration for its Ecto schemas. This can be done by running:

mix boruta.gen.migration
  1. Implement ResourceOwners context

In order to have user flows working, You need to implement Boruta.Oauth.ResourceOwners.

Here is an example implementation:

defmodule MyApp.ResourceOwners do
  @behaviour Boruta.Oauth.ResourceOwners

  alias Boruta.Oauth.ResourceOwner
  alias MyApp.Accounts.User
  alias MyApp.Repo

  @impl Boruta.Oauth.ResourceOwners
  def get_by(username: username) do
    with %User{id: id, email: email} <- Repo.get_by(User, email: username) do
      {:ok, %ResourceOwner{sub: id, username: email}}
    else
      _ -> {:error, "User not found."}
    end
  end
  def get_by(sub: sub) do
    with %User{id: id, email: email} = user <- Repo.get_by(User, id: sub) do
      {:ok, %ResourceOwner{sub: id, username: email}}
    else
      _ -> {:error, "User not found."}
    end
  end

  @impl Boruta.Oauth.ResourceOwners
  def check_password(resource_owner, password) do
    User.check_password(user, password)
  end

  @impl Boruta.Oauth.ResourceOwners
  def authorized_scopes(%ResourceOwner{}), do: []
end
  1. Configuration

Boruta provides several configuration options, to customize them you can add configurations in config.exs as following

config :boruta, Boruta.Oauth,
  repo: MyApp.Repo, # mandatory
  cache_backend: Boruta.Cache,
  contexts: [
    access_tokens: Boruta.Ecto.AccessTokens,
    clients: Boruta.Ecto.Clients,
    codes: Boruta.Ecto.Codes,
    resource_owners: MyApp.ResourceOwners, # mandatory for user flows
    scopes: Boruta.Ecto.Scopes
  ],
  max_ttl: [
    authorization_code: 60,
    access_token: 60 * 60 * 24
  ],
  token_generator: Boruta.TokenGenerator

Integration

This implementation follows an hexagonal architecture to invert dependencies to Application layer. In order to expose endpoints of an OAuth server with Boruta, you need implement the behaviour Boruta.Oauth.Application with all needed callbacks for token/2, authorize/2, introspect/2 and revoke/2 calls from Boruta.Oauth.

This library has specific interfaces to interact with Plug.Conn requests.

Here is an example of a token endpoint controller:

defmodule MyApp.OauthController do
  @behaviour Boruta.Oauth.Application
  ...
  def token(%Plug.Conn{} = conn, _params) do
    conn |> Oauth.token(__MODULE__)
  end

  @impl Boruta.Oauth.Application
  def token_success(conn, %TokenResponse{} = response) do
    conn
    |> put_view(OauthView)
    |> render("token.json", response: response)
  end

  @impl Boruta.Oauth.Application
  def token_error(conn, %Error{status: status, error: error, error_description: error_description}) do
    conn
    |> put_status(status)
    |> put_view(OauthView)
    |> render("error.json", error: error, error_description: error_description)
  end
  ...
end

Straightforward testing

You can also create a client and test it

alias Boruta.Ecto
alias Boruta.Oauth.Authorization
alias Boruta.Oauth.{ClientCredentialsRequest, Token}

# create a client
{:ok, %Ecto.Client{id: client_id, secret: client_secret}} = Ecto.Admin.create_client(%{authorization_code_ttl: 60, access_token_ttl: 60 * 60})
# obtain a token
{:ok, %Token{value: value}} = Authorization.token(%ClientCredentialsRequest{client_id: client_id, client_secret: client_secret})
# check token
{:ok, _token} = Authorization.AccessToken.authorize(value: value)

Guides

Some integration guides are provided as code samples.

Feedback

It is a work in progress, all feedbacks / feature requests / improvements are welcome