Current section

Files

Jump to
livebook lib livebook zta google_iap.ex
Raw

lib/livebook/zta/google_iap.ex

defmodule Livebook.ZTA.GoogleIAP do
@behaviour Livebook.ZTA
use GenServer
require Logger
import Plug.Conn
@assertion "x-goog-iap-jwt-assertion"
@renew_after 24 * 60 * 60 * 1000
@fields %{"sub" => :id, "name" => :name, "email" => :email}
defstruct [:req_options, :identity, :name]
def start_link(opts) do
identity = opts[:custom_identity] || identity(opts[:identity_key])
name = Keyword.fetch!(opts, :name)
options = [req_options: [url: identity.certs], identity: identity, name: name]
GenServer.start_link(__MODULE__, options, name: name)
end
@impl true
def authenticate(name, conn, _opts) do
token = get_req_header(conn, @assertion)
{identity, keys} = Livebook.ZTA.get(name)
{conn, authenticate_user(token, identity, keys)}
end
@impl true
def init(options) do
state = struct!(__MODULE__, options)
{:ok, renew(state)}
end
@impl true
def handle_info(:renew, state) do
{:noreply, renew(state)}
end
defp renew(state) do
Logger.debug("[#{inspect(__MODULE__)}] requesting #{inspect(state.req_options)}")
keys = Req.request!(state.req_options).body["keys"]
Process.send_after(self(), :renew, @renew_after)
Livebook.ZTA.put(state.name, {state.identity, keys})
state
end
defp authenticate_user(token, identity, keys) do
with [encoded_token] <- token,
{:ok, token} <- verify_token(encoded_token, keys),
:ok <- verify_iss(token, identity.iss, identity.key) do
for(
{k, v} <- token.fields,
new_k = @fields[k],
do: {new_k, v},
into: %{payload: token.fields}
)
else
_ -> nil
end
end
defp verify_token(token, keys) do
Enum.find_value(keys, :error, fn key ->
case JOSE.JWT.verify(key, token) do
{true, token, _s} -> {:ok, token}
_ -> nil
end
end)
end
defp verify_iss(%{fields: %{"iss" => iss, "aud" => key}}, iss, key), do: :ok
defp verify_iss(_, _, _), do: :error
defp identity(key) do
%{
key: key,
key_type: "aud",
iss: "https://cloud.google.com/iap",
certs: "https://www.gstatic.com/iap/verify/public_key-jwk",
assertion: "x-goog-iap-jwt-assertion",
email: "x-goog-authenticated-user-email",
user_identity: "https://www.googleapis.com/plus/v1/people/me"
}
end
end