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