Current section

Files

Jump to
ex_firebase_auth lib mock.ex
Raw

lib/mock.ex

defmodule ExFirebaseAuth.Mock do
@moduledoc """
This module will generate a public-private keypair and store it in ExFirebaseAuth's ETS tables.
You can create ID tokens identical to Firebase's tokens for use in integrating testing your auth
stack.
When enabled, tokens generated from this mock will be accepted by ExFirebaseAuth.Token.verify_token/1
## Enabling the mock
In order to prevent non-google tokens from being added to real-world environments, you need to
enable the mock in your app's configuration.
```elixir
config :ex_firebase_auth, :mock,
enabled: true # defaults to false
```
"""
@spec is_enabled? :: boolean()
@doc """
Returns whether mocking is enabled, returns false by default
"""
def is_enabled?, do: Keyword.get(mock_config(), :enabled, false)
@spec generate_and_store_key_pair :: any()
@doc """
Generates and stores a new key pair in ETS tables. **Note: this already gets called on app init,
you probably do not need this.**
"""
def generate_and_store_key_pair do
unless is_enabled?() do
raise "Cannot generate mocked token, because ExFirebaseAuth.Mock is not enabled in your config."
end
private_table = find_or_create_private_key_table()
public_table = ExFirebaseAuth.KeyStore.find_or_create_ets_table()
{kid, public_key, private_key} = generate_key()
:ets.insert(private_table, {kid, private_key})
:ets.insert(public_table, {kid, public_key})
end
@doc false
def generate_key do
private_key = JOSE.JWS.generate_key(%{"alg" => "RS256"})
public_key = JOSE.JWK.to_public(private_key)
kid = JOSE.JWK.thumbprint(:md5, public_key)
{kid, public_key, private_key}
end
@spec generate_token(String.t(), map) :: String.t()
@doc ~S"""
Generates a firebase-like ID token with the mock's private key. Will raise when mock is not enabled.
## Examples
iex> ExFirebaseAuth.Mock.generate_token("userid", %{"claim" => "value"})
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjEzMDA4MTkzODAsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlLCJpc3MiOiJqb2UifQ.shLcxOl_HBBsOTvPnskfIlxHUibPN7Y9T4LhPB-iBwM"
"""
def generate_token(sub, claims \\ %{}) do
unless is_enabled?() do
raise "Cannot generate mocked token, because ExFirebaseAuth.Mock is not enabled in your config."
end
{kid, jwk} = get_private_key()
jws = %{
"alg" => "RS256",
"kid" => kid
}
# Put exp claim, unless previously specified in claims
exp = DateTime.utc_now() |> DateTime.add(3600, :second) |> DateTime.to_unix()
claims = Map.put_new(claims, "exp", exp)
jwt =
Map.merge(claims, %{
"iss" => ExFirebaseAuth.Token.issuer(),
"sub" => sub
})
{_, payload} = JOSE.JWT.sign(jwk, jws, jwt) |> JOSE.JWS.compact()
payload
end
defp mock_config, do: Application.get_env(:ex_firebase_auth, :mock, [])
defp find_or_create_private_key_table do
case :ets.whereis(ExFirebaseAuth.Mock) do
:undefined -> :ets.new(ExFirebaseAuth.Mock, [:set, :public, :named_table])
table -> table
end
end
defp get_private_key do
case :ets.lookup(ExFirebaseAuth.Mock, :ets.first(ExFirebaseAuth.Mock)) do
[] -> raise "No private key set for ExFirebaseAuth.Mock, is mock enabled?"
[{_kid, _key} = value] -> value
end
end
end