lib/auth/access/index.ex

defmodule Rivet.Auth.Access do
  @moduledoc """
  Validation of tokens for accessing site (after refresh)

  Note: this is the start of the next iteration, and it will replace
  Rivet.Auth.Token
  """
  alias Rivet.Auth
  alias Rivet.Ident

  @doc """
  iex> alias Rivet.Auth
  iex> check([""], %Auth.Domain{})
  {:missing, %Auth.Domain{log: "Missing authorization header value"}}
  iex> check([], %Auth.Domain{})
  {:missing, %Auth.Domain{log: "Missing authorization header value"}}
  iex> check(["bearer token"], %Auth.Domain{})
  {:error, %Auth.Domain{log: "Invalid authorization"}}
  iex> check(["narf"], %Auth.Domain{})
  {:error, %Auth.Domain{log: "Authorization header exists but is not formatted properly"}}
  iex> check(["narf narf"], %Auth.Domain{})
  {:error, %Auth.Domain{log: "Invalid authorization type: narf"}}
  """

  @spec check(auth_header :: list(String.t()), Auth.Domain.t()) ::
          {:ok | :missing | :error, Auth.Domain.t()}
  # | def(check([], %Auth.Domain{} = auth),
  #     do: {:missing, %Auth.Domain{auth | log: "Missing authorization header"}}
  #   )
  def check([""], %Auth.Domain{} = auth),
    do: {:missing, %Auth.Domain{auth | log: "Missing authorization header value"}}

  def check([header], %Auth.Domain{} = auth) do
    with [type, tok] <- String.split(header, " ", parts: 2) do
      case String.downcase(type) do
        "bearer" -> {:bearer, tok}
        _ -> {:error, %Auth.Domain{auth | log: "Invalid authorization type: #{type}"}}
      end
    else
      _ ->
        {:error,
         %Auth.Domain{auth | log: "Authorization header exists but is not formatted properly"}}
    end
    |> process_authorization(auth)
  end

  def check(_, %Auth.Domain{} = auth),
    do: {:missing, %Auth.Domain{auth | log: "Missing authorization header value"}}

  # Standard bearer tokens for users
  defp process_authorization({:bearer, token}, auth) do
    case Auth.Token.Verify.jwt(token, :acc) do
      {:error, reason} ->
        {:error, %Auth.Domain{auth | log: reason}}

      {:ok, claims} ->
        auth = %Auth.Domain{auth | status: :authed, token: %{ref: token, claims: claims}}

        # this checks the validation token embedded within the refresh token
        case Auth.Token.Check.jwt(auth) |> Ident.User.Lib.check_user_status() do
          {:ok, %Auth.Domain{} = auth} -> {:ok, auth}
          # we passed signature, but the claims weren't right
          {:error, %Auth.Domain{} = auth} -> {:error, %Auth.Domain{auth | status: :unknown}}
        end
    end
  end

  defp process_authorization(pass, _), do: pass
end