Packages
livebook
0.18.1
0.19.8
0.19.7
0.19.6
0.19.5
0.19.4
0.19.3
0.19.2
0.19.1
0.19.0
0.18.6
0.18.5
0.18.4
0.18.3
0.18.2
0.18.1
0.18.0
0.17.3
0.17.2
0.17.1
0.17.0
0.16.4
0.16.3
0.16.2
0.16.1
0.16.0
0.15.5
0.15.4
0.15.3
0.15.2
0.15.1
0.15.0
0.14.7
0.14.6
0.14.5
0.14.4
0.14.3
0.14.2
0.14.1
0.14.0
0.14.0-rc.1
0.14.0-rc.0
0.13.3
0.13.2
0.13.1
0.13.0
0.12.1
0.12.0
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.0
0.9.3
0.9.2
0.9.1
0.9.0
0.8.2
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.6.3
0.6.2
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.1
0.4.0
0.3.2
0.3.1
0.3.0
0.2.3
0.2.2
0.2.1
0.2.0
0.1.2
0.1.1
0.1.0
Automate code & data workflows with interactive notebooks
Current section
Files
Jump to
Current section
Files
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