Current section
Files
Jump to
Current section
Files
lib/guardian_db.ex
defmodule GuardianDb do
@moduledoc """
GuardianDb is a simple module that hooks into guardian to prevent playback of tokens.
In vanilla Guardian, tokens aren't tracked so the main mechanism that exists to make a token inactive is to set the expiry and wait until it arrives.
GuardianDb takes an active role and stores each token in the database verifying it's presense (based on it's jti) when Guardian verifies the token.
If the token is not present in the DB, the Guardian token cannot be verified.
Provides a simple database storage and check for Guardian tokens.
- When generating a token, the token is stored in a database.
- When tokens are verified (channel, session or header) the database is checked for an entry that matches. If none is found, verification results in an error.
- When logout, or revoking the token, the corresponding entry is removed
"""
use Guardian.Hooks
defmodule Token do
@moduledoc """
A very simple model for storing tokens generated by guardian.
"""
use Ecto.Model
@primary_key {:jti, :string, autogenerate: false }
schema "guardian_tokens" do
field :aud, :string
field :iss, :string
field :sub, :string
field :exp, :integer
field :jwt, :string
field :claims, :string
timestamps
end
@doc """
Create a new new token based on the JWT and decoded claims
"""
def create!(claims, jwt) do
prepared_claims = claims |> Dict.put("jwt", jwt)
GuardianDb.repo.insert cast(%Token{}, prepared_claims, [], [:jti, :aud, :iss, :sub, :exp, :jwt])
end
@doc """
Purge any tokens that are expired. This should be done periodically to keep your DB table clean of clutter
"""
def purge_expired_tokens! do
timestamp = Guardian.Utils.timestamp
from(t in Token, where: t.exp < ^timestamp) |> GuardianDb.repo.delete_all
end
end
if !Dict.get(Application.get_env(:guardian_db, GuardianDb), :repo), do: raise "GuardianDb requires a repo"
@doc """
After the JWT is generated, stores the various fields of it in the DB for tracking
"""
def after_encode_and_sign(resource, type, claims, jwt) do
case Token.create!(claims, jwt) do
{ :error, _ } -> { :error, :token_storage_failure }
_ -> { :ok, { resource, type, claims, jwt } }
end
end
@doc """
When a token is verified, check to make sure that it is present in the DB.
If the token is found, the verification continues, if not an error is returned.
"""
def on_verify(claims, jwt) do
case repo.get_by(Token, jti: Dict.get(claims, "jti")) do
nil -> { :error, :token_not_found }
_token -> { :ok, { claims, jwt } }
end
end
@doc """
When logging out, or revoking a token, removes from the database so the token may no longer be used
"""
def on_revoke(claims, jwt) do
jti = Dict.get(claims, "jti")
model = repo.get_by(Token, jti: jti)
if model do
case repo.delete(model) do
{ :error, _ } -> { :error, :could_not_revoke_token }
nil -> { :error, :could_not_revoke_token }
_ -> { :ok, { claims, jwt } }
end
else
{ :ok, { claims, jwt } }
end
end
def repo do
Dict.get(Application.get_env(:guardian_db, GuardianDb), :repo)
end
end