# Guardian.DB

[![Build Status](](
[![Inline docs](](

Guardian.DB is an extension to vanilla Guardian that tracks tokens in your
application's database to prevent playback.

Support for `Guardian` 0.14.x is via the 0.8 release.

## Installation

Guardian.DB assumes that you are using the Guardian framework for authentication.

To install Guardian.DB, first add it to your `mix.exs` file:

defp deps do
    {:guardian_db, "~> 1.0"}

Then run `mix deps.get` on your terminal.

You will then need to add a migration:

run `mix guardian.db.gen.migration` to generate a migration.

**Do not run the migration yet,** we need to complete our setup first.

## Configuration

  config :guardian, Guardian.DB,
         repo: MyApp.Repo,
         schema_name: "guardian_tokens", # default
         token_types: ["refresh_token"], # store all token types if not set
         sweep_interval: 60 # default: 60 minutes

To sweep expired tokens from your db you should add `Guardian.DB.Token.SweeperServer` to your supervision tree.

  worker(Guardian.DB.Token.SweeperServer, [])

`Guardian.DB` works by hooking into the lifecycle of your token module.

You'll need to add it to:

* `after_encode_and_sign`
* `on_verify`
* `on_refresh`
* `on_revoke`

For example:

defmodule MyApp.AuthTokens do
  use Guardian, otp_app: :my_app

  # snip...

  def after_encode_and_sign(resource, claims, token, _options) do
    with {:ok, _} <- Guardian.DB.after_encode_and_sign(resource, claims["typ"], claims, token) do
      {:ok, token}

  def on_verify(claims, token, _options) do
    with {:ok, _} <- Guardian.DB.on_verify(claims, token) do
      {:ok, claims}

  def on_refresh({old_token, old_claims}, {new_token, new_claims}, _options) do
    with {:ok, _, _} <- Guardian.DB.on_refresh({old_token, old_claims}, {new_token, new_claims}) do
      {:ok, {old_token, old_claims}, {new_token, new_claims}}

  def on_revoke(claims, token, _options) do
    with {:ok, _} <- Guardian.DB.on_revoke(claims, token) do
      {:ok, claims}

Now run the migration and you'll be good to go.

### Considerations

Vanilla Guardian is already a very robust JWT solution. However, if your application needs the ability to immediately revoke and invalidate tokens that have already been generated, you need something like Guardian.DB to build upon Guardian.

In vanilla Guardian, you as a systems administrator have no way of revoking tokens that have already been generated. You can call `Guardian.revoke!`, but in vanilla Guardian that function does not actually do anything - it just provides hooks for other libraries, such as this one, to define more specific behavior. Discarding the token after something like a log out action is left up to the client application. If the client application does not discard the token, or does not log out, or the token gets stolen by a malicious script (because the client application stores it in localStorage, for instance), the only thing you can do is wait until the token expires. Depending on the scenario, this may not be acceptable.

With Guardian.DB, records of all generated tokens are kept in your application's database. During each request, the `Guardian.Plug.VerifyHeader` and `Guardian.Plug.VerifySession` plugs check the database to make sure the token is there. If it is not, the server returns a 401 Unauthorized response to the client. Furthermore, `Guardian.revoke!` behavior becomes enhanced, as it actually removes the token from the database. This means that if the user logs out, or you revoke their token (e.g. after noticing suspicious activity on the account), they will need to re-authenticate.

### Disadvantages

In vanilla Guardian, token verification is very light-weight. The only thing Guardian does is decode incoming tokens and make sure they are valid. This can make it much easier to horizontally scale your application, since there is no need to centrally store sessions and make them available to load balancers or other servers.

With Guardian.DB, every request requires a trip to the database, as Guardian now needs to ensure that a record of the token exists. In large scale applications this can be fairly costly, and can arguably eliminate the main advantage of using a JWT authentication solution, which is statelessness. Furthermore, session authentication already works this way, and in most cases there isn't a good enough reason to reinvent that wheel using JWTs.

In other words, once you have reached a point where you think you need Guardian.DB, it may be time to take a step back and reconsider your whole approach to authentication!