defmodule Boruta do @moduledoc """ Boruta is the core of an OAuth provider giving business logic of authentication and authorization. It is intended to follow RFCs: - [RFC 6749 - The OAuth 2.0 Authorization Framework](https://tools.ietf.org/html/rfc6749) - [RFC 7662 - OAuth 2.0 Token Introspection](https://tools.ietf.org/html/rfc7662) - [RFC 7009 - OAuth 2.0 Token Revocation](https://tools.ietf.org/html/rfc7009) 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. ## Installation 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 ``` 2. 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: sub, email: username} <- Repo.get_by(User, email: username) do {:ok, %ResourceOwner{sub: sub, username: username}} else _ -> {:error, "User not found."} end end def get_by(sub: sub) do with %User{} = user <- Repo.get_by(User, id: sub) do {:ok, %ResourceOwner{sub: sub, username: username}} 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 ``` 3. __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, contexts: [ access_tokens: Boruta.Ecto.AccessTokens, clients: Boruta.Ecto.Clients, codes: Boruta.Ecto.Codes, resource_owners: MyApp.ResourceOwners, scopes: Boruta.Ecto.Scopes ], expires_in: [ authorization_code: 60, access_token: 3600 ], token_generator: Boruta.TokenGenerator ``` ## Integration This implementation follows a pseudo 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` and `introspect/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 ``` ## Feedback It is a work in progress, all feedbacks / feature requests / improvements are welcome -> [me](mailto:io.pascal.knoth@gmail.com) """ end