Authex v0.3.1 Authex behaviour View Source

Defines an auth module.

This module provides a simple set of tools for the authorization and authentication required by a typical API through use of JSON web tokens. To get started, we need to define our auth module:

defmodule MyApp.Auth do
  use Authex, otp_app: :my_app
end

We must then add the auth module to our supervision tree.

children = [
  MyApp.Auth
]

Configuration

While our auth module is defined, we will need to further configure it to our requirements. 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. We should use the init/1 callback to configure this:

defmodule MyApp.Auth do
  use Authex, otp_app: :my_app

  def init(config) do
    secret = System.get_env("AUTH_SECRET")
    config = Keyword.put(config, :secret, secret)

    {:ok, config}
  end
end

Any other config can either be set with the start_link/1 or init/1 callbacks, or via application config. Below are some of the values available:

config :my_app, MyApp.Auth, [
  # REQUIRED
  # The secret used to sign tokens with.
  secret: "mysecret",

  # OPTIONAL
  # A blacklist repo, or false if disabled.
  blacklist: false,
  # A banlist repo, or false if disabled.
  banlist: false,
  # 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 plug called when an unauthorized status is determined.
  unauthorized: Authex.UnauthorizedPlug
  # The plug called when an forbidden status is determined.
  forbidden: Authex.ForbiddenPlug
]

Tokens

At the heart of Authex is the Authex.Token struct. This struct is simply a wrapper around the typical JWT claims. The only additional item is the :scopes and :meta key. There are 3 base actions required for these tokens - creation, signing, and verification.

Creating

We can easily create token structs using the token/2 function.

MyApp.Auth.token(sub: 1, scopes: ["admin/read"])

The above would create a token struct for a user with an id of 1 and with "admin/read" authorization.

Signing

Once we have a token struct, we can sign it using the sign/2 function to create a compact token binary. This is what we will use for authentication and authorization for our API.

[sub: 1, scopes: ["admin/read"]]
|> MyApp.Auth.token()
|> MyApp.Auth.sign()

Verifying

Once we have a compact token binary, we can verify it and turn it back to an token struct using the verify/2 function.

[sub: 1, scopes: ["admin/read"]]
|> MyApp.Auth.token()
|> MyApp.Auth.sign()
|> MyApp.Auth.verify()

Serializers

Typically, we want to be able to create tokens from another source of data. This could be something like a User struct. We also will want to take a token and turn it back into a User struct.

To do this, we must create a serializer. A serializer is simply a module that adopts the Authex.Serializer behaviour. For more information on creating serializers, please see the Authex.Serializer documention.

Once we have created our serializer, we define it in our config.

config :my_app, MyApp.Auth, [
  serializer: MyApp.Auth.UserSerializer,
]

We can now easily create tokens and compact tokens from our custom data using the for_token/2 and for_compact_token/3 functions.

user = %MyApp.User{id: 1, scopes: []}

{:ok, token} = MyApp.Auth.for_token(user) # returns a token struct
{:ok, compact_token} = MyApp.Auth.for_compact_token(user) # returns a compact token

We can also turn tokens and compact tokens back into our custom data using the from_token/2 and from_compact_token/2 functions.

user = %MyApp.User{id: 1, scopes: []}

{:ok, token} = MyApp.Auth.for_token(user)
{:ok, user} = MyApp.Auth.from_token(token)

{:ok, compact_token} = MyApp.Auth.for_compact_token(user)
{:ok, user} = MyApp.Auth.from_compact_token(compact_token)

Repositories

Usually, use of JSON web tokens requires some form of persistence to blacklist tokens through their :jti claim. Authex also adds the ability to ban a token through its :sub claim.

To do this, we must create a repository. A repository is simply a module that adopts the Authex.Repo behaviour. For more information on creating repositories, please see the Authex.Repo documention.

Once we have created our blacklist or banlist repo, we define it in our config.

config :my_app, MyApp.Auth, [
  blacklist: MyApp.Auth.Blacklist,
  banlist: MyApp.Auth.Banlist
]

During the verification process used by verify/2, any blacklist or banlist defined in our config will be checked against. Please be aware of any performance penatly that may be incurred through use of database-backed repo's without use of caching.

Plugs

Authex provides a number of plugs to handle the typical authentication and authorization process required by an API using your auth module.

For more information on handling authentication, please see the Authex.AuthenticationPlug documention.

For more information on handling authorization, please see the Authex.AuthorizationPlug documention.

Link to this section Summary

Callbacks

Bans a token subject

Checks whether a token subject is banned

Blacklists a token jti

Checks whether a token jti is blacklisted

Fetches a config value

Gets the current scopes from a Plug.Conn

Gets the current user from a Plug.Conn

Converts a resource into a compact token

Converts a resource into an Authex.Token struct

Verifies and converts a compact token into a resource

Converts an Authex.Token struct into a resource

A callback executed when the auth process starts

Saves the config that is currently associated with our auth module

Sets the config that is used with our auth module

Sets a single config that is used with our auth module

Signs a token, creating a compact token

Starts the auth process

Creates a new token

Unbans a token subject

Unblacklists a token jti

Verifies a compact token

Link to this section Types

Link to this type

alg() View Source
alg() :: :hs256 | :hs384 | :hs512

Link to this type

signer_option() View Source
signer_option() :: {:alg, alg()} | {:secret, binary()}

Link to this type

signer_options() View Source
signer_options() :: [signer_option()]

Link to this type

verifier_option() View Source
verifier_option() ::
  {:alg, alg()}
  | {:time, integer()}
  | {:secret, binary()}
  | {:banlist, Authex.Banlist.t()}
  | {:blacklist, Authex.Blacklist.t()}

Link to this type

verifier_options() View Source
verifier_options() :: [verifier_option()]

Link to this section Callbacks

Link to this callback

ban(token) View Source
ban(token :: Authex.Token.t()) :: :ok | :error

Bans a token subject.

This uses the banlist repo defined in the auth config. The key is the :sub key in the token.

Returns :ok on success, or :error on failure.

Example

MyApp.Auth.ban(token)
Link to this callback

banned?(token) View Source
banned?(token :: Authex.Token.t()) :: boolean()

Checks whether a token subject is banned.

This uses the banlist repo defined in the auth config. The key is the :sub key in the token.

Returns a boolean.

Example

MyApp.Auth.banned?(token)
Link to this callback

blacklist(token) View Source
blacklist(token :: Authex.Token.t()) :: :ok | :error

Blacklists a token jti.

This uses the blaclist repo defined in the auth config. The key is the :jti key in the token.

Returns :ok on success, or :error on failure.

Example

MyApp.Auth.blacklist(token)
Link to this callback

blacklisted?(token) View Source
blacklisted?(token :: Authex.Token.t()) :: boolean()

Checks whether a token jti is blacklisted.

This uses the blaclist repo defined in the auth config. The key is the :jti key in the token.

Returns a boolean.

Example

MyApp.Auth.blacklisted?(token)
Link to this callback

config(key, default) View Source
config(key :: atom(), default :: any()) :: any()

Fetches a config value.

Link to this callback

current_scopes(arg0) View Source
current_scopes(Plug.Conn.t()) :: {:ok, list()} | :error

Gets the current scopes from a Plug.Conn.

Link to this callback

current_user(arg0) View Source
current_user(Plug.Conn.t()) :: {:ok, term()} | :error

Gets the current user from a Plug.Conn.

Link to this callback

for_compact_token(term, token_opts, signer_options) View Source
for_compact_token(
  term(),
  token_opts :: Authex.Token.options(),
  signer_options()
) :: {:ok, Authex.Token.compact()} | {:error, term()}

Converts a resource into a compact token.

Returns {:ok, compact_token} or {:error, reason}

Options

Please see the options available in token/2.

Example

{:ok, compact_token} = MyApp.Auth.for_compact_token(user)
Link to this callback

for_token(term, options) View Source
for_token(term(), options :: Authex.Token.options()) ::
  {:ok, Authex.Token.t()} | {:error, term()}

Converts a resource into an Authex.Token struct.

This uses the serializer defined in the auth config. It will invoke the Authex.Serializer.for_token/2 callback defined in the serializer module. Please see the Authex.Serializer documentation for more details on implementing a serializer.

Returns {:ok, token} or {:error, reason}

Options

Please see the options available in token/2.

Example

{:ok, token} = MyApp.Auth.for_token(user)
Link to this callback

from_compact_token(compact_token, verifier_options) View Source
from_compact_token(compact_token :: Authex.Token.compact(), verifier_options()) ::
  {:ok, term()} | {:error, atom()}

Verifies and converts a compact token into a resource.

Once verified, this invokes from_token/2 with the verified token. Please see from_token/2 for additional details.

Returns {:ok, resource} or {:error, reason}

Options

Please see the options available in verify/2. You can also include any additional options your serializer might need.

Example

{:ok, user} = MyApp.Auth.from_compact_token(compact_token)
Link to this callback

from_token(token, options) View Source
from_token(token :: Authex.Token.t(), options :: Keyword.t()) ::
  {:ok, term()} | {:error, term()}

Converts an Authex.Token struct into a resource.

This uses the serializer defined in the auth config. It will invoke the Authex.Serializer.from_token/2 callback defined in the serializer module. Please see the Authex.Serializer documentation for more details on implementing a serializer.

Returns {:ok, resource} or {:error, reason}

Options

Any additional options your serializer might need.

Example

{:ok, user} = MyApp.Auth.from_token(token)
Link to this callback

init(config) View Source
init(config :: Keyword.t()) :: {:ok, Keyword.t()}

A callback executed when the auth process starts.

This should be used to dynamically set any config during runtime - such as the secret key used to sign tokens with.

Returns {:ok, config}

Example

def init(config) do
  secret = System.get_env("AUTH_SECRET")
  config = Keyword.put(config, :secret, secret)

  {:ok, config}
end
Link to this callback

save_config() View Source
save_config() :: :ok | :error

Saves the config that is currently associated with our auth module.

Link to this callback

save_config(keyword) View Source
save_config(keyword()) :: :ok | :error

Sets the config that is used with our auth module.

Link to this callback

save_config(atom, any) View Source
save_config(atom(), any()) :: :ok | :error

Sets a single config that is used with our auth module.

Signs a token, creating a compact token.

The compact token is a binary that can be used for authentication and authorization purposes. Typically, this would be placed in an HTTP header, such as:

Authorization: Bearer mytoken

Returns compact_token or raises an Authex.Error.

Options

  • :secret - The secret key to sign the token with.
  • :alg - The algorithm to sign the token with.

Any option provided would override the default set in the config.

Link to this callback

start_link(config) View Source
start_link(config :: Keyword.t()) :: GenServer.on_start()

Starts the auth process.

Returns {:ok, pid} on success.

Returns {:error, {:already_started, pid}} if the auth process is already started or {:error, term} in case anything else goes wrong.

Options

See the configuration in the moduledoc for options.

Link to this callback

token(claims, options) View Source
token(claims :: Authex.Token.claims(), options :: Authex.Token.options()) ::
  Authex.Token.t()

Creates a new token.

A token is a struct that wraps the typical JWT claims but also adds a couple new fields. Please see the Authex.Token documentation for more details.

Returns an Authex.Token struct,

Options

  • :time - The base time (timestamp format) in which to use.
  • :ttl - The time-to-live for the token in seconds. The lifetime is based on the time provided via the options, or the current time if not provided.

Example

MyApp.Auth.token(sub: 1, scopes: ["admin/read"])
Link to this callback

unban(token) View Source
unban(token :: Authex.Token.t()) :: :ok | :error

Unbans a token subject.

This uses the banlist repo defined in the auth config. The key is the :sub key in the token.

Returns :ok on success, or :error on failure.

Example

MyApp.Auth.unban(token)
Link to this callback

unblacklist(token) View Source
unblacklist(token :: Authex.Token.t()) :: :ok | :error

Unblacklists a token jti.

This uses the blaclist repo defined in the auth config. The key is the :jti key in the token.

Returns :ok on success, or :error on failure.

Example

MyApp.Auth.unblacklist(token)
Link to this callback

verify(compact_token, options) View Source
verify(compact_token :: Authex.Token.compact(), options :: verifier_options()) ::
  {:ok, Authex.Token.t()} | {:error, term()}

Verifies a compact token.

Verification is a multi-step process that ensures:

  1. The token has not been tampered with.
  2. The current time is not before the nbf value.
  3. The current time is not after the exp value.
  4. The token jti is not included in the blacklist (if provided).
  5. The token sub is not included in the banlist (if provided).

If all checks pass, the token is deemed verified.

Options

  • :time - The base time (timestamp format) in which to use.
  • :secret - The secret key to verify the token with.
  • :alg - The algorithm to verify the token with
  • :banlist - The banlist module to verify with.
  • :blacklist - The blacklist module to verify with.

Any option provided would override the default set in the config.

Returns {:ok, token} or {:error, reason}

Example

{:ok, token} = MyApp.Auth.verify(compact_token)