View Source Google Certificates

Elixir CI

A Lightweight GenServer that stores and caches Google's Public Certificates.

The Google Certificates library makes it easy to verify and validate a JWT issued by Google. See the how to use with the joken library section below for an example on how to accomplish this using this library combined with the Joken library.

For more details, see Google's backend-auth documentation, specifically the verify the integrity of the id token section.

The default behavior is to use the JWK format (/oauth2/v3/certs) for certificates, but the PEM (/oauth2/v1/certs) format can be used if specified.

installation

Installation

This package can be installed by adding google_certs to your list of dependencies in mix.exs:

def deps do
  [
    {:google_certs, "~> 0.1"}
  ]
end

usage

Usage

Invoke GoogleCerts.get/0 or GoogleCerts.fetch/1 where needed.

more-info

More Info

GoogleCerts.CertificateCache is an Agent that will automatically be started and hydrated so that calls to GoogleCerts.get/0 or GoogleCerts.fetch/1 will return the cached results.

Every time GoogleCerts.get/0 or GoogleCerts.fetch/1 is invoked, the expiration of the cached certificates is checked. If the certificates are expired then a new network request is automatically issued to Google's API to refresh the cache.

optional

Optional

If you wish to start GoogleCerts.CertificateCache manually you can set GOOGLE_CERTS_ENABLE_AUTO_START=false or auto_start?: false and add it to your supervision tree.

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

    opts = [strategy: :one_for_one, name: Directory.Supervisor]
    Supervisor.start_link(children, opts)
  end

There are three publicity available functions that are helpful.

  1. GoogleCerts.get/0- Get cached certificates
  2. GoogleCerts.fetch/1 - Get a certificate by it's kid (useful with Joken.peek_header/1 to and Joken.Signer.create/3 to create a signer)
  3. GoogleCerts.refresh/1 - Refresh a GoogleCerts.Certificates struct if the certificates are expired. Intended for internal use.
iex> GoogleCerts.get()                                              # get all certificates
iex> GoogleCerts.fetch("257f6a5828d1e4a3a6a03fcd1a2461db9593e624")  # get a certificate by its kid
iex> GoogleCerts.refresh(%GoogleCerts.Certificates{})               # refresh a set of certificates if they are expired

how-to-use-with-the-joken-library

How to use with the Joken library

  1. Create a custom verify hook
  2. Register your custom verify hook with your JWTManager (custom module that use Joken.Config )
  3. Use your JWTManager to verify and validate a JWT issued from Google.
    1. See ueberauth and ueberauth_google packages for retrieving a JWT from Google as part of your authentication.

Create a custom verify hook

defmodule 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

Register your custom verify hook with your JWTManager

defmodule 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(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

Use your JWTManager to verify and validate a JWT issued from Google

iex> jwt = "eyJhbGciOiJSUzI1..."
iex> {:ok, claims} = JWTManager.verify_and_validate(jwt)

configuration-optional

Configuration (Optional)

See GoogleCerts.Env for all possible configurations. Most settings can be set using either system environment variables or elixir configurations.

# config/config.exs
config :google_certs, version: 1 # Use PEM format instead of JWK format. defaults to 3 for JWK
# bash
GOOGLE_CERTS_API_VERSION=1 iex -S mix phx.server # Use PEM format instead of JWK format. defaults to 3 for JWK