Verifying Firebase ID Token in Elixir/Erlang

Viewed 680

I've set up Firebase for frontend authentication and I am sending the ID token, which is a JWT, to my phoenix backend.

I am now stuck trying to verify the JWT.

Google instructions regarding the matter are here. tldr, grab the publicly accessible certificate and use it to verify the JWT signature was signed with the correct private key.

I have this so far

def verify(token) do
  {:ok, resp} = HTTPoison.get(@cert_url)
  %{body: body} = resp
  body = Poison.Parser.parse!(body, %{})
  {:ok, header} = Joken.peek_header(token)
  cert = body[header["kid"]]
end

I'm lost there. Do I need to convert the public certificate to a public key? How do I create a Joken.Signer with RS256 signing algorithm and the public certificate? I am open to solutions that don't use Joken as well.

Thank you!

2 Answers

Slightly more idiomatic version of @fetching_water's solution, which makes use of more pattern matching and uses Hackney instead of HTTPoison:

@cert_url "https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com"

def verify!(token) do
  {:ok, 200, _headers, ref} = :hackney.get(@cert_url)
  {:ok, body} = :hackney.body(ref)
  {:ok, %{"kid" => kid}} = Joken.peek_header(token)

  {true, %{fields: fields}, _} =
    body
    |> Jason.decode!()
    |> JOSE.JWK.from_firebase()
    |> Map.fetch!(kid)
    |> JOSE.JWT.verify(token)

  fields
end

I found the JOSE library :D

To finish up the code…

def verify(token) do
  {:ok, resp} = HTTPoison.get(@cert_url)
  %{body: body} = resp
  certs = Poison.Parser.parse!(body, %{})
  {:ok, header} = Joken.peek_header(token)
  jwks = JOSE.JWK.from_firebase(certs)
  jwk = jwks[header["kid"]] |> JOSE.JWK.to_map |> elem(1)
  {true, jose_jwt, _} = JOSE.JWT.verify(jwk, token)
  fields = JOSE.JWT.to_map(jose_jwt) |> elem(1)
  {:ok, fields}
end

Elixir code can probably be cleaned up ^_^

Related