fireproofsocks

fireproofsocks

Using Joken to validate Google JWTs

I’ve been trying out the Joken package to work with JWTs. Right now I’m unable to verify the JWTs that Google generates during Oauth. Has anyone implemented this? I think Joken has all the pieces, I just can’t figure out the winning combination. I’ve been reading this: Authenticate with a backend server  |  Web guides  |  Google for Developers

Google’s public keys are available in JWK format https://www.googleapis.com/oauth2/v3/certs
and in PEM format: https://www.googleapis.com/oauth2/v1/certs

I can get Google JWT, and I’ve tried to set up a module for these particular Google JWTs:

defmodule GoogleJwt do
  
  use Joken.Config, default_signer: nil # no signer

  def token_config do
    default_claims()
  end
end

But the following always generates an error of invalid signature:

MyGoogleJwt.verify_and_validate(idtoken)

Anyone have any tips?

Most Liked

vinagrito1

vinagrito1

Heya guys. So I’m another soul that struggled with something that was supposed to be much easier for a while. And finally landed here.
So after trying all the libraries out there I’m sticking to JOSE. Thx @dom for mentioning it. As @fireproofsocks pointed out previously

I can run something like:

token = "from google oauth"
pem = "--- relevant key copied from https://www.googleapis.com/oauth2/v1/certs"
jwk = JOSE.JWK.from_pem(pem)
JOSE.JWT.verify_strict(jwk, ["RS256"], token)

Will do the job. Thank you all for the inputs

victorolinasc

victorolinasc

Sorry for such a delayed response. I’ve replied @fireproofsocks on Joken repo about RS keys configuration and released a more detailed configuration of asymmetric keys guide.

To validate tokens from Google, Microsoft and other that have an OpenID Connect certs endpoint (or similar) with a published JWKS, one can use JokenJwks.

You’d do simply:

defmodule MyGoogleToken do
  use Joken.Config

  add_hook(JokenJwks, strategy: MyGoogleStrategy)

  def token_config do
    # your config here with what you want to validate from the token
  end
end

defmodule MyGoogleJwksStrategy do
  use JokenJwks.DefaultStrategyTemplate

  def init_opts(opts), do: [jwks_url: "https://www.googleapis.com/oauth2/v1/certs"]
end

defmodule MyApp do
  use Application

  def start(_type, _args) do
     children = 
      [
        MyGoogleStrategy,
        # others
      ]
      # other start logic
  end
spencerdcarlson

spencerdcarlson

If you are just interested in using Google’s certs to validate a JWT issued by them, I made the google_certs hex package.

The Google Certs package will download, cache, and auto refresh Google’s certs. Both v1 PEM format and v3 JWK format are supported and are ready to work with Joken (see GoogleCerts.fetch/1). Here is the example from the how to use with joken section in the docs:

defmodule MyApp.Application do
  @moduledoc false

  use Application
  alias GoogleCerts.CertificateCache
  
  def start(_type, _args) do
    children = [
      CertificateCache
    ]

    opts = [strategy: :one_for_one, name: MyApp.Supervisor]
    Supervisor.start_link(children, opts)
  end
end
defmodule MyApp.Crypto.VerifyHook do
  @moduledoc false

  use Joken.Hooks

  @impl true
  def before_verify(_options, {jwt, %Joken.Signer{} = _signer}) do
    with {:ok, %{"kid" => kid}} <- Joken.peek_header(jwt),
         {:ok, algorithm, key} <- GoogleCerts.fetch(kid) do
      {:cont, {jwt, Joken.Signer.create(algorithm, key)}}
    else
      error -> {:halt, {:error, :no_signer}}
    end
  end
end
defmodule MyApp.Crypto.JWTManager do
  @moduledoc false

  use Joken.Config, default_signer: nil

  @iss "https://accounts.google.com"
  
  # your google client id (usually ends in *.apps.googleusercontent.com)
  defp aud, do: Application.get_env(:my_app, :google_client_id) 

  # reference your custom verify hook here
  add_hook(MyApp.Crypto.VerifyHook) 

  @impl Joken.Config
  def token_config do
    default_claims(skip: [:aud, :iss])
    |> add_claim("iss", nil, &(&1 == @iss))
    |> add_claim("aud", nil, &(&1 == aud()))
  end
end
# anywhere in your app you can verify and validate a Google issued JWT
iex> jwt = "eyJhbGciOiJSUzI1..." # Google issued JWT (api call, uberauth, etc)
iex> {:ok, claims} = JWTManager.verify_and_validate(jwt)

Where Next?

Popular in Questions Top

WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New
sen
Hi All, I set a environment variables in dev.exs , like below code. when i start server, how can i set the ${enable} value? thanks. d...
New
JeremM34
Hello, how can I check the Phoenix version ? Thanks !
New
vac
Hi, I’m quite new in Elixir and I’m trying to format a string to a PEM format. I have the certificate value like MIIDBTCCAe2...... and I...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
New
jason.o
In the code below, if the create action is not set to accept “extra_key” as an input, it errors out with a message shown above. Is there ...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
hariharasudhan94
Lets say I have map like this fetching from my database %{"_id" =&gt; #BSON.ObjectId&lt;58eb1a7a9ad169198c3dXXXX&gt;, "email" =&gt; ...
New

Other popular topics Top

vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
sen
Hi All, I set a environment variables in dev.exs , like below code. when i start server, how can i set the ${enable} value? thanks. d...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
Fl4m3Ph03n1x
About me? ( if you have nothing better to do than reading about some random guy in the internet :stuck_out_tongue: ) Hello all, this is ...
New
dblack
I’ve got an issue with an app and I’ve no idea of how to troubleshoot it. I’m hoping someone here might have seen something similar. I p...
New
komlanvi
Hi everyone, I was playing with phoenix liveView but I run into an issue. I have a form and want to validate each input text when the te...
New
sergio
Kind of like when jquery came out, it was super necessary. Existing drag and drop libraries have a bunch of baggage to support old browse...
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New

We're in Beta

About us Mission Statement