plug_indie/lib/token.ex

111 lines
3.1 KiB
Elixir
Raw Normal View History

2024-11-29 09:42:27 +00:00
defmodule PlugIndie.Token do
require Logger
def verify(
access_token,
token_endpoint,
required_scope,
supported_scopes,
own_hostname,
user_agent
) do
case do_verify_token(access_token, token_endpoint, user_agent) do
{:ok, %{status: 200, body: body}} ->
verify_token_response(body, required_scope, supported_scopes, own_hostname)
{:ok, %{status: status}} ->
{:error, :request_error, status}
{:error, %{code: code}} ->
Logger.error("Token endpoint responded with unexpected code: #{inspect(code)}")
{:error, :request_error, code}
{:error, %{reason: reason}} ->
Logger.error("Could not reach token endpoint: #{inspect(reason)}")
{:error, :request_error, reason}
error ->
Logger.error("Unexpected error: #{inspect(error)}")
{:error, :request_error, "Internal Server Error"}
end
end
defp do_verify_token(access_token, token_endpoint, user_agent) do
client =
Tesla.client([
Tesla.Middleware.JSON,
{Tesla.Middleware.Headers,
[
{"User-Agent", user_agent},
{"Authorization", "Bearer #{access_token}"},
{"Accept", "application/json"}
]}
])
Tesla.get(client, token_endpoint)
end
defp verify_token_response(
%{
me: host_uri,
scope: scope,
client_id: client_id,
issued_at: _issued_at,
issued_by: _issued_by,
nonce: _nonce
},
required_scope,
supported_scopes,
own_hostname
) do
Logger.info("Host-URI: '#{host_uri}'")
Logger.info("ClientId: '#{client_id}'")
Logger.info("Scopes: '#{scope}'")
with :ok <- verify_hostname_match(host_uri, own_hostname),
:ok <- verify_scope_support(scope, required_scope, supported_scopes) do
:ok
else
{:error, name, reason} ->
Logger.error("Could not verify token response: #{reason}")
{:error, name, reason}
end
end
defp verify_hostname_match(host_uri, own_hostname) do
hostnames_match? = get_hostname(host_uri) == own_hostname
case hostnames_match? do
true ->
:ok
_ ->
Logger.warning("Hostnames do not match: Given #{host_uri}, Actual: #{own_hostname}")
{:error, "verify_hostname_match", "hostname does not match"}
end
end
defp get_hostname(host_uri) do
host_uri |> URI.parse() |> Map.get(:host)
end
defp verify_scope_support(_scopes, nil, _supported_scopes), do: :ok
defp verify_scope_support(scopes, required_scope, supported_scopes)
when not is_nil(required_scope) do
required = Enum.member?(supported_scopes, required_scope)
requested = Enum.member?(String.split(scopes), required_scope)
cond do
required && requested ->
:ok
!required ->
{:error, "verify_scope_support", "scope '#{required_scope}' is not supported"}
!requested ->
{:error, "verify_scope_support", "scope '#{required_scope}' was not requested"}
end
end
end