Current section

Files

Jump to
joken lib joken plug.ex
Raw

lib/joken/plug.ex

defmodule Joken.Plug do
import Joken
alias Joken.Token
@moduledoc """
A Plug for signing and verifying authentication tokens.
## Usage
There are two possible scenarios:
1. Same configuration for all routes
2. Per route configuration
In the first scenario just add this plug before the dispatch plug.
defmodule MyRouter do
use Plug.Router
plug Joken.Plug, on_verifying: &verify_function/1
plug :match
plug :dispatch
post "/user" do
# will only execute here if token is present and valid
end
match _ do
# will only execute here if token is present and valid
end
end
In the second scenario, you will need at least plug ~> 0.14 in your deps.
Then you must plug this AFTER :match and BEFORE :dispatch.
defmodule MyRouter do
use Plug.Router
# route options
@skip_token_verification %{joken_skip: true}
plug :match
plug Joken.Plug, config_module: MyJWTConfig
plug :dispatch
post "/user" do
# will only execute here if token is present and valid
end
# see options section below
match _, private: @skip_token_verification do
# will NOT try to validate a token
end
end
## Options
This plug accepts the following options in its initialization:
- `on_verifying`: a function used to verify the token. Receives a Token and must return a Token
- `on_error` (optional): a function that will be called with `conn` and `message`. Must
return a tuple containing the conn and a binary representing the 401 response. If it's a map,
it will be turned into json, otherwise, it will be returned as is.
When using this with per route options you must pass a private map of options
to the route. The keys that Joken will look for in that map are:
- `joken_skip`: skips token validation
- `joken_on_verifying`: Same as `on_verifying` above. Overrides
`on_verifying` if it was defined on the Plug
- `joken_on_error`: Same as `on_error` above. Overrides
`on_error` if it was defined on the Plug
"""
import Plug.Conn
@doc false
def init(opts) do
on_verifying = Keyword.get(opts, :on_verifying)
on_error = Keyword.get(opts, :on_error, &Joken.Plug.default_on_error/2)
{on_verifying, on_error}
end
@doc false
def call(conn, { on_verifying, on_error }) do
unless Map.has_key?(conn.private, :joken_on_verifying) do
conn = put_private(conn, :joken_on_verifying, on_verifying)
end
unless Map.has_key?(conn.private, :joken_on_error) do
conn = put_private(conn, :joken_on_error, on_error)
end
if Map.get(conn.private, :joken_skip, false) do
conn
else
parse_auth(conn, get_req_header(conn, "authorization"))
end
end
defp parse_auth(conn, ["Bearer " <> incoming_token]) do
payload_fun = Map.get(conn.private, :joken_on_verifying)
verified_token = payload_fun.()
|> with_compact_token(incoming_token)
|> verify
evaluate(conn, verified_token)
end
defp parse_auth(conn, _header) do
send_401(conn, "Unauthorized")
end
defp evaluate(conn, %Token{ error: nil } = token) do
assign(conn, :joken_claims, get_claims(token))
end
defp evaluate(conn, %Token{ error: message }) do
send_401(conn, message)
end
defp send_401(conn, message) do
on_error = conn.private[:joken_on_error]
{conn, message } = case on_error.(conn, message) do
{conn, map} when is_map(map) ->
create_json_response(conn, map)
response ->
response
end
conn
|> send_resp(401, message)
|> halt
end
defp create_json_response(conn, map) do
conn = put_resp_content_type(conn, "application/json")
json = Poison.encode!(map)
{conn, json}
end
@doc false
def default_on_error(conn, message) do
{ conn, message }
end
end