balls/lib/balls_pds/plug/wac_authentication_plug.ex

55 lines
1.4 KiB
Elixir
Raw Permalink Normal View History

2024-12-02 11:03:06 +00:00
defmodule BallsPDS.Plug.WACAuthenticationPlug do
import Plug.Conn
alias BallsPDS.JWT
alias BallsPDS.WAC
def init(opts), do: opts
def call(conn, _opts) do
with {:subject, nil} <- {:subject, conn.assigns[:subject]},
{:info, {:ok, subject}} <- {:info, verify_auth_info(conn)} do
conn
|> assign(:subject, subject)
else
{:subject, _} ->
# Already set somewhere, ignore
conn
end
end
defp verify_auth_info(conn) do
with {:extract, {:ok, jwt}} when not is_nil(jwt) <- {:extract, extract_jwt(conn)},
{:jwk, {:ok, jwk}} <- {:jwk, get_jwk(jwt)},
{:verify, {:ok, %{"sub" => subject}}} <- {:verify, JWT.verify_jwt(jwt, jwk)} do
{:ok, subject}
else
{:extract, {:ok, nil}} ->
# No auth provided, okay for public stuff.
{:ok, nil}
{:verify, {:error, _} = error} ->
error
end
end
defp get_jwk(jwt) do
with {:peek, {:ok, %{:subject => subject, :id => key_id}}} <-
{:peek, JWT.extract_key_info(jwt)} do
if subject == Application.get_env(:balls_pds, :owner_ap_id) do
{:ok, JWT.generate_my_jwk()}
else
WAC.cached_query_public_key(subject, key_id)
end
end
end
defp extract_jwt(conn) do
with ["Bearer " <> jwt] <- get_req_header(conn, "authorization") do
{:ok, jwt}
else
[] -> {:ok, nil}
unexpected_result -> {:error, unexpected_result}
end
end
end