Current section
Files
Jump to
Current section
Files
lib/simple_token_authentication.ex
defmodule SimpleTokenAuthentication do
@moduledoc """
A plug that checks for presence of a simple token for authentication
"""
@behaviour Plug
import Plug.Conn
require Logger
def init(opts), do: opts
def call(conn, opts) do
realm = Keyword.get(opts, :auth_realm)
val = get_auth_header(conn)
token_map =
case :persistent_term.get(store(realm), nil) do
nil ->
build_token_map(realm)
val ->
val
end
case secure_lookup(token_map, val) do
{:ok, service_name} ->
Logger.metadata(service_name: service_name)
assign(conn, :simple_token_auth_service, service_name)
:error ->
conn
|> put_resp_content_type("application/json")
|> send_resp(401, ~s({ "error": "Invalid shared key" }))
|> halt()
end
end
defp secure_lookup(token_map, val) do
Enum.find_value(token_map, :error, fn {token, service_name} ->
if Plug.Crypto.secure_compare(token, val), do: {:ok, service_name}
end)
end
defp get_auth_header(conn) do
case get_req_header(conn, "authorization") do
[val | _] -> val
_ -> ""
end
end
defp build_token_map(nil) do
global = token()
services = service_tokens(nil)
token_map =
for {service, tokens} <- [{:global, global} | services],
token <- List.wrap(tokens),
token != "",
reduce: %{} do
acc ->
Map.put(acc, token, service)
end
:persistent_term.put(store(nil), token_map)
token_map
end
defp build_token_map(realm) do
services = service_tokens(realm)
token_map =
for {service, tokens} <- services,
token <- List.wrap(tokens),
token != "",
reduce: %{} do
acc ->
Map.put(acc, token, service)
end
:persistent_term.put(store(realm), token_map)
token_map
end
defp service_tokens(nil) do
:simple_token_authentication
|> Application.get_env(:service_tokens)
|> List.wrap()
end
defp service_tokens(realm) do
:simple_token_authentication
|> Application.get_env(realm)
|> Keyword.get(:service_tokens)
|> List.wrap()
end
defp token do
:simple_token_authentication
|> Application.get_env(:token)
|> List.wrap()
end
defp store(nil) do
:default
end
defp store(realm) do
realm
end
end