README.md

# Authex

[![Build Status](https://travis-ci.org/nsweeting/authex.svg?branch=master)](https://travis-ci.org/nsweeting/authex)
[![Authex Version](https://img.shields.io/hexpm/v/authex.svg)](https://hex.pm/packages/authex)

Authex is a simple JWT authentication and authorization library for Elixir.

## Installation

The package can be installed by adding `authex` to your list of dependencies in `mix.exs`:

```elixir
def deps do
  [
    {:authex, "~> 0.1.0"}
  ]
end
```

## Documentation

See [HexDocs](https://hexdocs.pm/authex) for additional documentation.

## Getting Started

Before starting, we should configure Authex. At a minimum, we need to add a secret
from which our tokens will be signed with. There is a convenient mix task available
for this.

```
mix authex.gen.secret
```

We should now add this secret to our config. In production this should be set via
an env var. By default, authex will pick up the env var `AUTH_SECRET` if we have
not set one via config.

```elixir
config :authex, [
  # REQUIRED
  # The secret used to sign tokens with.
  secret: "mysecret",

  # OPTIONAL
  # A blacklist module, or false if disabled.
  blacklist: false,
  # The default serializer module.
  serializer: Authex.Serializer.Basic,
  # The default algorithm used to sign tokens.
  default_alg: :hs256,
  # The default iss claim used in tokens.
  default_iss: nil,
  # The default aud claim used in tokens.
  default_aud: nil,
  # The default time to live for tokens in seconds.
  default_ttl: 3600,
  # The default module, function, and arg used to generate the jti claim.
  jti_mfa: {UUID, :uuid4, [:hex]}
]
```

The above config is all the defaults "out of the box".

## Usage by Example

Here are some basic examples on how to use Authex.

Create a token using the default serializer. This assumes users have an `id` field.

```elixir
MyApp.User
|> MyApp.Repo.get(1)
|> Authex.for_token()
```

Create a token with the sub and iss claim set. The token will also have a time
to live of 60 seconds. `Authex.token/1` returns an `Authex.Token` struct. `Authex.sign/1`
creates a compact token from an `Authex.Token` struct.

```elixir
user = MyApp.Repo.get(MyApp.User, 1)
token = Authex.token([sub: user.id, iss: "myapp"], [ttl: 60])
Authex.sign(token)
```

Verify a compact token and return an `Authex.Token` struct.

```elixir
MyApp.User
|> MyApp.Repo.get(1)
|> Authex.for_token()
|> Authex.verify()
```

Verify a compact token and return a resource created from a serializer.

```elixir
MyApp.User
|> MyApp.Repo.get(1)
|> Authex.for_token()
|> Authex.from_token()
```

Create a custom Serializer.

```elixir
defmodule MyApp.TokenSerializer do
  use Authex.Serializer

  def from_token(%Authex.Token{sub: sub, scopes: scopes}) do
    %MyApp.User{id: sub, scopes: scopes}
  end

  def for_token(%MyApp.User{id: id, scopes: scopes}) do
    Authex.Token.new([sub: id, scopes: scopes])
  end
end

```

Authenticate a Phoenix controller using a custom serializer. `Authex.Plug.Authenticate`
looks for the `Authenicate: Bearer mytoken` header. It will then verify,
and deserialize the token using the provided serializer.

If any of these steps fails, it will put a 401 status and halt the conn.

Otherwise, the plug will place the value returned from the serializer into the conn.
You can access this value again using `Authex.current_user/1`.

```elixir
defmodule MyApp.Web.UserController do
  use MyApp.Web, :controller

  plug :authenticate

  def show(conn, _params) do
    with {:ok, %{id: id}} <- Authex.current_user(conn),
         {:ok, user} <- MyApp.Users.get(id)
    do
      render(conn, "show.json", user: user)
    end
  end

  defp authenticate(conn, _opts) do
    opts = Authex.Plug.Authentication.init([serializer: MyApp.TokenSerializer])
    Authex.Plug.Authentication.call(conn, opts)
  end
end
```

Authorize a user to access a particular endpoint using their token scopes. Authorization
works by combining the "permits" with the "type" of request that is being made.

For example, with our controller below, we are permitting "user" and "admin"
access. The show action would be a `GET` request, and would therefore be a "read"
type.

So, in order to access the show action, our token would require one of the
two following scopes: `["user/read", "admin/read"]`.

If a user fails to meet the scope requirements with their token, it will put a
403 status and halt the conn.

Requests are bucketed under the following types:

  * "GET" - "read"
  * "HEAD" - "read"
  * "PUT" - "write"
  * "PATCH" - "write"
  * "POST" - "write"
  * "DELETE" - "delete"

```elixir
defmodule MyApp.Web.UserController do
  use MyApp.Web, :controller

  plug :authenticate
  plug :authorize, permits: ["user", "admin"]

  def show(conn, _params) do
    with {:ok, %{id: id}} <- Authex.current_user(conn),
         {:ok, user} <- MyApp.Users.get(id)
    do
      render(conn, "show.json", user: user)
    end
  end

  defp authenticate(conn, _opts) do
    opts = Authex.Plug.Authentication.init([serializer: MyApp.TokenSerializer])
    Authex.Plug.Authentication.call(conn, opts)
  end

  defp authorize(conn, opts) do
    opts = Authex.Plug.Authorization.init(opts)
    Authex.Plug.Authorization.call(conn, opts)
  end
end
```