Google Certificates
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
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
GoogleCerts.CertificateCache
is an Agent
so you can simply add it to your supervision tree and Google's certificates will be downloaded and cached on startup.
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.
GoogleCerts.get/0
- Get cached certificatesGoogleCerts.fetch/1
- Get a certificate by it's kid (useful withJoken.peek_header/1
to andJoken.Signer.create/3
to create a signer)GoogleCerts.refresh/1
- Refresh aGoogleCerts.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
- Create a custom verify hook
- Register your custom verify hook with your JWTManager (custom module that
use Joken.Config
) Use your JWTManager to verify and validate a JWT issued from Google.
- 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)
# config/config.exs
config :google_certs, version: 1 # Use PEM format instead of JWK format. defaults to 3 for JWK