Current section

Files

Jump to
ex_firebase_auth lib token.ex
Raw

lib/token.ex

defmodule ExFirebaseAuth.Token do
defp get_public_key(keyid) do
case :ets.lookup(ExFirebaseAuth.KeyStore, keyid) do
[{_keyid, key}] ->
key
[] ->
nil
end
end
@spec issuer :: String.t()
@doc ~S"""
Returns the configured issuer
## Examples
iex> ExFirebaseAuth.Token.issuer()
"https://securetoken.google.com/project-123abc"
"""
def issuer, do: Application.fetch_env!(:ex_firebase_auth, :issuer)
@spec verify_token(String.t()) ::
{:error, String.t()} | {:ok, String.t(), JOSE.JWT.t()}
@doc ~S"""
Verifies a token agains google's public keys. Returns {:ok, user_id, claims} if successful. {:error, _} otherwise.
## Examples
iex> ExFirebaseAuth.Token.verify_token("ey.some.token")
{:ok, "user id", %{}}
iex> ExFirebaseAuth.Token.verify_token("ey.some.token")
{:error, "Invalid JWT header, `kid` missing"}
"""
def verify_token(token_string) do
issuer = issuer()
with {:jwtheader, %{fields: %{"kid" => kid}}} <- peek_token_kid(token_string),
# read key from store
{:key, %JOSE.JWK{} = key} <- {:key, get_public_key(kid)},
# check if verify returns true and issuer matches
{:verify, {true, %{fields: %{"iss" => ^issuer, "sub" => sub, "exp" => exp}} = data, _}} <-
{:verify, JOSE.JWT.verify(key, token_string)},
# Verify exp date
{:verify, {:ok, _}} <- {:verify, verify_expiry(exp)} do
{:ok, sub, data}
else
:invalidjwt ->
{:error, "Invalid JWT"}
{:jwtheader, _} ->
{:error, "Invalid JWT header, `kid` missing"}
{:key, _} ->
{:error, "Public key retrieved from google was not found or could not be parsed"}
{:verify, {false, _, _}} ->
{:error, "Invalid signature"}
{:verify, {true, _, _}} ->
{:error, "Signed by invalid issuer"}
{:verify, {:expired, _}} ->
{:error, "Expired JWT"}
{:verify, _} ->
{:error, "None of public keys matched auth token's key ids"}
end
end
defp peek_token_kid(token_string) do
{:jwtheader, JOSE.JWT.peek_protected(token_string)}
rescue
_ -> :invalidjwt
end
defp verify_expiry(exp) do
cond do
exp > DateTime.utc_now() |> DateTime.to_unix() -> {:ok, exp}
true -> {:expired, exp}
end
end
end