defmodule Rivet.Auth.Refresh do
@moduledoc """
Tooling for the second phase of auth: Refresh
TODO: Extract from Phoenix/WebSvc better, abstract so conn isn't needed
This will require a module that is imported into WebSvc -BJG
"""
alias Rivet.Auth
@doc """
1. extract the validaton token from the refresh token
2. decode & verify the validation token is ours & good
3. using the sub:uuid from the validation token, verify the signature on the ref token
4. update connection data (or abort)
```
iex> assure(%Auth.Domain{}, nil)
{:error, %Auth.Domain{log: "Invalid refresh request"}}
```
"""
def assure(%Auth.Domain{} = auth, %{"client_assertion" => refresh_token})
when is_binary(refresh_token) do
auth = %Auth.Domain{auth | token: %{ref: refresh_token}}
# do this manually first
Auth.Token.extract(refresh_token, :claims)
|> extract_validation_token(auth)
|> check_refresh_token
end
def assure(_arg, _params) do
{:error, %Auth.Domain{log: "Invalid refresh request"}}
end
##############################################################################
@doc ~S"""
```
iex> {:error, %Auth.Domain{log: "Invalid authorization"}} = extract_validation_token({:ok, %{sub: "cas2:asdf"}}, %Auth.Domain{})
iex> {:error, %Auth.Domain{log: "Unable to match validation token subject: \"wut\""}} = extract_validation_token({:ok, "wut"}, %Auth.Domain{})
iex> {:error, %Auth.Domain{log: "narf!"}} = extract_validation_token({:error, "narf!"}, %Auth.Domain{})
```
"""
def extract_validation_token({:ok, %{sub: "cas2:" <> validation_token}}, auth) do
# now check the signature
case Auth.Token.Verify.jwt(validation_token, :val) do
{:error, reason} ->
{:error, %Auth.Domain{auth | log: reason}}
{:ok, result} ->
valauth = %Auth.Domain{
auth
| status: :authed,
token: %{ref: validation_token, claims: result}
}
# this checks the validation token embedded within the refresh token
case Auth.Token.Check.jwt(valauth) do
{:ok, %Auth.Domain{} = valauth} ->
{:ok, auth, valauth}
{:error, %Auth.Domain{} = auth} ->
{:error, auth}
end
end
end
def extract_validation_token({:error, reason}, auth) when is_binary(reason) do
{:error, %Auth.Domain{auth | log: reason}}
end
# turn it into an error if we didn't match above
def extract_validation_token({:ok, args}, auth),
do:
{:error,
%Auth.Domain{auth | log: "Unable to match validation token subject: #{inspect(args)}"}}
##############################################################################
@doc """
```
iex> {:error, "narf!"} = check_refresh_token({:error, "narf!"})
```
"""
def check_refresh_token(
{:ok, %Auth.Domain{token: %{ref: token}} = refauth,
%Auth.Domain{factor: %Rivet.Ident.Factor{value: secret}} = valauth}
)
when not is_nil(secret) do
case Auth.Token.Verify.jwt(token, [secret]) do
{:ok, _} ->
{:ok, %Auth.Domain{refauth | status: :authed}, valauth}
{:error, msg} ->
{:error, %Auth.Domain{refauth | log: "Unable to verify refresh token signature: #{msg}"}}
end
end
def check_refresh_token(pass = {:error, _reason}), do: pass
end