mirror of
https://github.com/livebook-dev/livebook.git
synced 2025-09-05 20:44:30 +08:00
ZTA revamp
* Rename SessionIdentity to PassThrough and make it part of ZTA * Compute the ID at the Plug level, rather than ZTA level and avoid storing it twice * Stop the user "avatar" from flashing on initial render * Do not duplicate identity data inside user data, rather keep them distinct
This commit is contained in:
parent
50f9bb2420
commit
29c5cb1904
21 changed files with 132 additions and 86 deletions
|
@ -1,20 +1,17 @@
|
|||
# Authentication
|
||||
|
||||
## Introduction
|
||||
Livebook has three levels of authentication:
|
||||
|
||||
Livebook's authentication covers all pages for creating, writing, and managing notebooks.
|
||||
* Instance authentication: this authenticates the user on all routes of your Livebook instance, including deployed notebooks and the admin section. This is done via Zero Trust Authentication and typically used when deploying Livebook to production. See the "Deployment" section on the sidebar for more information.
|
||||
|
||||
Livebook's default authentication method is token authentication. A token is automatically generated at startup and printed to the logs.
|
||||
* Admin authentication: this authenticates access to Livebook admin interface, where users can create, write, and manage notebooks. Both password and token authentication are provided.
|
||||
|
||||
* Deployed notebook authentication: additionally, when deploying notebooks as applications, each application may be password protected with a unique password. Only users authenticated as admin or with the password will be able to access them.
|
||||
|
||||
## Admin authentication
|
||||
|
||||
Livebook's default admin authentication method is token authentication. A token is automatically generated at startup and printed to the logs.
|
||||
|
||||
You may optionally enable password-based authentication by setting the environment variable `LIVEBOOK_PASSWORD` on startup or deployment. It must be at least 12 characters.
|
||||
|
||||
To disable authentication altogether, you may set the environment variable `LIVEBOOK_TOKEN_ENABLED` to `false`.
|
||||
|
||||
## Securing deployed notebooks
|
||||
|
||||
When you deploy a notebook as an application, the deployed application is not covered by Livebook's token/password authentication. In such cases, you have two options:
|
||||
|
||||
* You can set a password when deploying your notebook
|
||||
|
||||
* You can enable proxy authentication when deploying inside a cloud infrastructure.
|
||||
See the "Deployment" section on the sidebar for more information
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Authentication with Basic Auth
|
||||
|
||||
Setting up Basic Authentication will protect all routes of your notebook. It is particularly useful for adding authentication to deployed notebooks. Basic Authentication is provided in addition to [Livebook's authentication](../authentication.md) for authoring notebooks.
|
||||
Setting up Basic Authentication is a simple mechanism for protecting all routes of your Livebook instance with a single username-password combo. However, because this password is shared across all users, this authentication mechanism cannot be used to identity users and more robust authentication methods provided by Livebook should be preferred. Basic Authentication occurs in addition to [Livebook's authentication](../authentication.md) for deployed notebooks and admins.
|
||||
|
||||
## How to
|
||||
|
||||
|
@ -15,7 +15,7 @@ livebook server
|
|||
|
||||
## Livebook Teams
|
||||
|
||||
[Livebook Teams](https://livebook.dev/teams/) users have access to airgapped notebook deployment via Docker, with pre-configured Zero Trust Authentication, shared team secrets, and file storages.
|
||||
[Livebook Teams](https://livebook.dev/teams/) users can deploy notebooks with the click of a button with pre-configured Zero Trust Authentication, shared team secrets, and file storages. Both online and airgapped deployment mechanisms are supported.
|
||||
|
||||
Furthermore, if you are deploying multi-session apps via [Livebook Teams](https://livebook.dev/teams/), you can programmatically access data from the authenticated user by calling [`Kino.Hub.app_info/0`](https://hexdocs.pm/kino/Kino.Hub.html#app_info/0).
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Authentication with Cloudflare
|
||||
|
||||
Setting up Cloudflare authentication will protect all routes of your notebook. It is particularly useful for adding authentication to deployed notebooks. Cloudflare authentication is provided in addition to [Livebook's authentication](../authentication.md) for authoring notebooks.
|
||||
Setting up Cloudflare authentication will protect all routes of your Livebook instance. It is particularly useful for adding authentication to Livebook instances with deployed notebooks. Cloudflare authentication occurs in addition to [Livebook's authentication](../authentication.md) for deployed notebooks and admins.
|
||||
|
||||
Once Cloudflare is enabled, we recommend leaving the "/public" route of your instances still public. This route is used for integration with the [Livebook Badge](https://livebook.dev/badge/) and other conveniences.
|
||||
|
||||
|
@ -17,7 +17,7 @@ https://developers.cloudflare.com/cloudflare-one/.
|
|||
|
||||
## Livebook Teams
|
||||
|
||||
[Livebook Teams](https://livebook.dev/teams/) users have access to airgapped notebook deployment via Docker, with pre-configured Zero Trust Authentication, shared team secrets, and file storages.
|
||||
[Livebook Teams](https://livebook.dev/teams/) users can deploy notebooks with the click of a button with pre-configured Zero Trust Authentication, shared team secrets, and file storages. Both online and airgapped deployment mechanisms are supported.
|
||||
|
||||
Furthermore, if you are deploying multi-session apps via [Livebook Teams](https://livebook.dev/teams/), you can programmatically access data from the authenticated user by calling [`Kino.Hub.app_info/0`](https://hexdocs.pm/kino/Kino.Hub.html#app_info/0).
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Authentication with Google IAP
|
||||
|
||||
Setting up Google IAP authentication will protect all routes of your notebook. It is particularly useful for adding authentication to deployed notebooks. Google IAP authentication is provided in addition to [Livebook's authentication](../authentication.md) for authoring notebooks.
|
||||
Setting up Google IAP authentication will protect all routes of your Livebook instance. It is particularly useful for adding authentication to Livebook instances with deployed notebooks. Google IAP authentication occurs in addition to [Livebook's authentication](../authentication.md) for deployed notebooks and admins.
|
||||
|
||||
Once Google IAP is enabled, we recommend leaving the "/public" route of your instances still public. This route is used for integration with the [Livebook Badge](https://livebook.dev/badge/) and other conveniences.
|
||||
|
||||
|
@ -17,7 +17,7 @@ For more details about how to find your JWT audience, see https://cloud.google.c
|
|||
|
||||
## Livebook Teams
|
||||
|
||||
[Livebook Teams](https://livebook.dev/teams/) users have access to airgapped notebook deployment via Docker, with pre-configured Zero Trust Authentication, shared team secrets, and file storages.
|
||||
[Livebook Teams](https://livebook.dev/teams/) users can deploy notebooks with the click of a button with pre-configured Zero Trust Authentication, shared team secrets, and file storages. Both online and airgapped deployment mechanisms are supported.
|
||||
|
||||
Furthermore, if you are deploying multi-session apps via [Livebook Teams](https://livebook.dev/teams/), you can programmatically access data from the authenticated user by calling [`Kino.Hub.app_info/0`](https://hexdocs.pm/kino/Kino.Hub.html#app_info/0).
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Authentication with Tailscale
|
||||
|
||||
Setting up Tailscale authentication will protect all routes of your notebook. It is particularly useful for adding authentication to deployed notebooks. Tailscale authentication is provided in addition to [Livebook's authentication](../authentication.md) for authoring notebooks.
|
||||
Setting up Tailscale authentication will protect all routes of your Livebook instance. It is particularly useful for adding authentication to Livebook instances with deployed notebooks. Tailscale authentication occurs in addition to [Livebook's authentication](../authentication.md) for deployed notebooks and admins.
|
||||
|
||||
Once Tailscale is enabled, we recommend leaving the "/public" route of your instances still public. This route is used for integration with the [Livebook Badge](https://livebook.dev/badge/) and other conveniences.
|
||||
|
||||
|
@ -42,7 +42,7 @@ livebook server
|
|||
|
||||
## Livebook Teams
|
||||
|
||||
[Livebook Teams](https://livebook.dev/teams/) users have access to airgapped notebook deployment via Docker, with pre-configured Zero Trust Authentication, shared team secrets, and file storages.
|
||||
[Livebook Teams](https://livebook.dev/teams/) users can deploy notebooks with the click of a button with pre-configured Zero Trust Authentication, shared team secrets, and file storages. Both online and airgapped deployment mechanisms are supported.
|
||||
|
||||
Furthermore, if you are deploying multi-session apps via [Livebook Teams](https://livebook.dev/teams/), you can programmatically access data from the authenticated user by calling [`Kino.Hub.app_info/0`](https://hexdocs.pm/kino/Kino.Hub.html#app_info/0).
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Authentication with Teleport
|
||||
|
||||
Setting up Teleport authentication will protect all routes of your notebook. It is particularly useful for adding authentication to deployed notebooks. Teleport authentication is provided in addition to [Livebook's authentication](../authentication.md) for authoring notebooks.
|
||||
Setting up Teleport authentication will protect all routes of your Livebook instance. It is particularly useful for adding authentication to Livebook instances with deployed notebooks. Teleport authentication occurs in addition to [Livebook's authentication](../authentication.md) for deployed notebooks and admins.
|
||||
|
||||
## How to
|
||||
|
||||
|
@ -17,7 +17,7 @@ on how Teleport authentication works.
|
|||
|
||||
## Livebook Teams
|
||||
|
||||
[Livebook Teams](https://livebook.dev/teams/) users have access to airgapped notebook deployment via Docker, with pre-configured Zero Trust Authentication, shared team secrets, and file storages.
|
||||
[Livebook Teams](https://livebook.dev/teams/) users can deploy notebooks with the click of a button with pre-configured Zero Trust Authentication, shared team secrets, and file storages. Both online and airgapped deployment mechanisms are supported.
|
||||
|
||||
Furthermore, if you are deploying multi-session apps via [Livebook Teams](https://livebook.dev/teams/), you can programmatically access data from the authenticated user by calling [`Kino.Hub.app_info/0`](https://hexdocs.pm/kino/Kino.Hub.html#app_info/0).
|
||||
|
||||
|
|
|
@ -45,6 +45,8 @@ defmodule Livebook.Config do
|
|||
}
|
||||
]
|
||||
|
||||
@identity_provider_no_id [Livebook.ZTA.BasicAuth, Livebook.ZTA.PassThrough]
|
||||
|
||||
@identity_provider_type_to_module Map.new(@identity_providers, fn provider ->
|
||||
{Atom.to_string(provider.type), provider.module}
|
||||
end)
|
||||
|
@ -296,8 +298,8 @@ defmodule Livebook.Config do
|
|||
"""
|
||||
@spec identity_provider_read_only?() :: boolean()
|
||||
def identity_provider_read_only?() do
|
||||
{type, _module, _key} = Livebook.Config.identity_provider()
|
||||
Map.has_key?(identity_provider_type_to_module(), type)
|
||||
{_type, module, _key} = Livebook.Config.identity_provider()
|
||||
module not in @identity_provider_no_id
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
@ -703,7 +705,7 @@ defmodule Livebook.Config do
|
|||
def identity_provider!(env) do
|
||||
case System.get_env(env) do
|
||||
nil ->
|
||||
{:session, LivebookWeb.ZTA.SessionIdentity, :unused}
|
||||
{:session, Livebook.ZTA.PassThrough, :unused}
|
||||
|
||||
"custom:" <> module_key ->
|
||||
destructure [module, key], String.split(module_key, ":", parts: 2)
|
||||
|
|
|
@ -45,7 +45,7 @@ defmodule Livebook.Users.User do
|
|||
|
||||
def changeset(user, attrs \\ %{}) do
|
||||
user
|
||||
|> cast(attrs, [:id, :name, :email, :hex_color])
|
||||
|> validate_required([:id, :name, :hex_color])
|
||||
|> cast(attrs, [:name, :email, :hex_color])
|
||||
|> validate_required([:hex_color])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,4 +1,50 @@
|
|||
defmodule Livebook.ZTA do
|
||||
@type name :: atom()
|
||||
|
||||
@typedoc """
|
||||
A metadata of keys returned by zero-trust authentication provider.
|
||||
|
||||
The following keys are supported:
|
||||
|
||||
* `:id` - a string that uniquely identifies the user
|
||||
* `:name` - the user name
|
||||
* `:email` - the user email
|
||||
* `:payload` - the provider payload
|
||||
|
||||
Note that none of the keys are required. The metadata returned depends
|
||||
on the provider.
|
||||
"""
|
||||
@type metadata :: %{
|
||||
optional(:id) => String.t(),
|
||||
optional(:name) => String.t(),
|
||||
optional(:email) => String.t(),
|
||||
optional(:payload) => map()
|
||||
}
|
||||
|
||||
@doc """
|
||||
Each provider must specify a child specification for its processes.
|
||||
|
||||
The `:name` and `:identity_key` keys are expected.
|
||||
"""
|
||||
@callback child_spec(name: name(), identity_key: String.t()) :: Supervisor.child_spec()
|
||||
|
||||
@doc """
|
||||
Authenticates against the given name.
|
||||
|
||||
It will return one of:
|
||||
|
||||
* `{non_halted_conn, nil}` - the authentication failed and you must
|
||||
halt the connection and render the appropriate report
|
||||
|
||||
* `{halted_conn, nil}` - the authentication failed and the connection
|
||||
was modified accordingly to request the credentials
|
||||
|
||||
* `{non_halted_conn, metadata}` - the authentication succeed and the
|
||||
following metadata about the user is available
|
||||
|
||||
"""
|
||||
@callback authenticate(name(), Plug.Conn.t(), keyword()) :: {Plug.Conn.t(), metadata() | nil}
|
||||
|
||||
@doc false
|
||||
def init do
|
||||
:ets.new(__MODULE__, [:named_table, :public, :set, read_concurrency: true])
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
defmodule Livebook.ZTA.BasicAuth do
|
||||
@behaviour Livebook.ZTA
|
||||
|
||||
@impl true
|
||||
def child_spec(opts) do
|
||||
%{id: __MODULE__, start: {__MODULE__, :start_link, [opts]}}
|
||||
end
|
||||
|
@ -12,6 +15,7 @@ defmodule Livebook.ZTA.BasicAuth do
|
|||
:ignore
|
||||
end
|
||||
|
||||
@impl true
|
||||
def authenticate(name, conn, _options) do
|
||||
{username, password} = Livebook.ZTA.get(name)
|
||||
conn = Plug.BasicAuth.basic_auth(conn, username: username, password: password)
|
||||
|
@ -19,7 +23,7 @@ defmodule Livebook.ZTA.BasicAuth do
|
|||
if conn.halted do
|
||||
{conn, nil}
|
||||
else
|
||||
{conn, %{payload: %{}}}
|
||||
{conn, %{}}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
defmodule Livebook.ZTA.Cloudflare do
|
||||
@behaviour Livebook.ZTA
|
||||
|
||||
use GenServer
|
||||
require Logger
|
||||
import Plug.Conn
|
||||
|
@ -16,6 +18,7 @@ defmodule Livebook.ZTA.Cloudflare do
|
|||
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)
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
defmodule Livebook.ZTA.GoogleIAP do
|
||||
@behaviour Livebook.ZTA
|
||||
|
||||
use GenServer
|
||||
require Logger
|
||||
import Plug.Conn
|
||||
|
@ -16,6 +18,7 @@ defmodule Livebook.ZTA.GoogleIAP do
|
|||
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)
|
||||
|
|
13
lib/livebook/zta/pass_through.ex
Normal file
13
lib/livebook/zta/pass_through.ex
Normal file
|
@ -0,0 +1,13 @@
|
|||
defmodule Livebook.ZTA.PassThrough do
|
||||
@behaviour Livebook.ZTA
|
||||
|
||||
@impl true
|
||||
def child_spec(_opts) do
|
||||
%{id: __MODULE__, start: {Function, :identity, [:ignore]}}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def authenticate(_, conn, _) do
|
||||
{conn, %{}}
|
||||
end
|
||||
end
|
|
@ -1,6 +1,8 @@
|
|||
defmodule Livebook.ZTA.Tailscale do
|
||||
@behaviour Livebook.ZTA
|
||||
require Logger
|
||||
|
||||
@impl true
|
||||
def child_spec(opts) do
|
||||
%{id: __MODULE__, start: {__MODULE__, :start_link, [opts]}}
|
||||
end
|
||||
|
@ -19,6 +21,7 @@ defmodule Livebook.ZTA.Tailscale do
|
|||
:ignore
|
||||
end
|
||||
|
||||
@impl true
|
||||
def authenticate(name, conn, _opts) do
|
||||
remote_ip = to_string(:inet_parse.ntoa(conn.remote_ip))
|
||||
tailscale_address = Livebook.ZTA.get(name)
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
defmodule Livebook.ZTA.Teleport do
|
||||
@behaviour Livebook.ZTA
|
||||
|
||||
use GenServer
|
||||
require Logger
|
||||
|
||||
|
@ -21,6 +23,7 @@ defmodule Livebook.ZTA.Teleport do
|
|||
GenServer.start_link(__MODULE__, options, name: name)
|
||||
end
|
||||
|
||||
@impl true
|
||||
def authenticate(name, conn, _opts) do
|
||||
token = Plug.Conn.get_req_header(conn, @assertion)
|
||||
jwks = Livebook.ZTA.get(name)
|
||||
|
|
|
@ -16,8 +16,6 @@ defmodule LivebookWeb.UserPlug do
|
|||
import Plug.Conn
|
||||
import Phoenix.Controller
|
||||
|
||||
alias Livebook.Users.User
|
||||
|
||||
@impl true
|
||||
def init(opts), do: opts
|
||||
|
||||
|
@ -31,17 +29,26 @@ defmodule LivebookWeb.UserPlug do
|
|||
|
||||
defp ensure_user_identity(conn) do
|
||||
{_type, module, _key} = Livebook.Config.identity_provider()
|
||||
|
||||
{conn, identity_data} = module.authenticate(LivebookWeb.ZTA, conn, [])
|
||||
|
||||
if identity_data do
|
||||
put_session(conn, :identity_data, identity_data)
|
||||
else
|
||||
conn
|
||||
|> put_status(:forbidden)
|
||||
|> put_view(LivebookWeb.ErrorHTML)
|
||||
|> render("403.html", %{status: 403})
|
||||
|> halt()
|
||||
cond do
|
||||
identity_data ->
|
||||
# Ensure we have a unique ID to identify this user/session.
|
||||
id =
|
||||
identity_data[:id] || get_session(conn, :identity_data)[:id] ||
|
||||
Livebook.Utils.random_long_id()
|
||||
|
||||
put_session(conn, :identity_data, Map.put(identity_data, :id, id))
|
||||
|
||||
conn.halted ->
|
||||
conn
|
||||
|
||||
true ->
|
||||
conn
|
||||
|> put_status(:forbidden)
|
||||
|> put_view(LivebookWeb.ErrorHTML)
|
||||
|> render("403.html", %{status: 403})
|
||||
|> halt()
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -51,9 +58,10 @@ defmodule LivebookWeb.UserPlug do
|
|||
if Map.has_key?(conn.req_cookies, "lb_user_data") do
|
||||
conn
|
||||
else
|
||||
identity_data = get_session(conn, :identity_data)
|
||||
user_data = User.new() |> client_user_data() |> Map.merge(identity_data)
|
||||
encoded = user_data |> Jason.encode!() |> Base.encode64()
|
||||
encoded =
|
||||
%{"name" => nil, "hex_color" => Livebook.EctoTypes.HexColor.random()}
|
||||
|> Jason.encode!()
|
||||
|> Base.encode64()
|
||||
|
||||
# We disable HttpOnly, so that it can be accessed on the client
|
||||
# and set expiration to 5 years
|
||||
|
@ -62,13 +70,6 @@ defmodule LivebookWeb.UserPlug do
|
|||
end
|
||||
end
|
||||
|
||||
defp client_user_data(user) do
|
||||
user
|
||||
|> Map.from_struct()
|
||||
|> Map.delete(:id)
|
||||
|> Map.delete(:payload)
|
||||
end
|
||||
|
||||
# Copies user_data from cookie to session, so that it's
|
||||
# accessible to LiveViews
|
||||
defp mirror_user_data_in_session(conn) when conn.halted, do: conn
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
defmodule LivebookWeb.ZTA.SessionIdentity do
|
||||
# This module implements the ZTA contract specific to Livebook cookies
|
||||
|
||||
import Plug.Conn
|
||||
|
||||
def authenticate(_, conn, _) do
|
||||
if id = get_session(conn, :current_user_id) do
|
||||
{conn, %{id: id}}
|
||||
else
|
||||
user_id = Livebook.Utils.random_long_id()
|
||||
{put_session(conn, :current_user_id, user_id), %{id: user_id}}
|
||||
end
|
||||
end
|
||||
|
||||
def child_spec(_opts) do
|
||||
%{id: __MODULE__, start: {Function, :identity, [:ignore]}}
|
||||
end
|
||||
end
|
|
@ -39,9 +39,9 @@ defmodule Livebook.ConfigTest do
|
|||
assert Config.identity_provider!("TEST_IDENTITY_PROVIDER") == {:custom, Module, nil}
|
||||
end)
|
||||
|
||||
with_env([TEST_IDENTITY_PROVIDER: "custom:LivebookWeb.ZTA.SessionIdentity:extra"], fn ->
|
||||
with_env([TEST_IDENTITY_PROVIDER: "custom:Livebook.ZTA.PassThrough:extra"], fn ->
|
||||
assert Config.identity_provider!("TEST_IDENTITY_PROVIDER") ==
|
||||
{:custom, LivebookWeb.ZTA.SessionIdentity, "extra"}
|
||||
{:custom, Livebook.ZTA.PassThrough, "extra"}
|
||||
end)
|
||||
|
||||
with_env([TEST_IDENTITY_PROVIDER: "cloudflare:123"], fn ->
|
||||
|
|
|
@ -14,15 +14,6 @@ defmodule Livebook.Users.UserTest do
|
|||
assert get_field(changeset, :hex_color) == "#000000"
|
||||
end
|
||||
|
||||
test "given empty name returns an error" do
|
||||
user = build(:user)
|
||||
attrs = %{"name" => ""}
|
||||
changeset = User.changeset(user, attrs)
|
||||
|
||||
refute changeset.valid?
|
||||
assert "can't be blank" in errors_on(changeset).name
|
||||
end
|
||||
|
||||
test "given invalid color returns an error" do
|
||||
user = build(:user)
|
||||
attrs = %{"hex_color" => "#invalid"}
|
||||
|
|
|
@ -21,7 +21,7 @@ defmodule Livebook.ZTA.BasicAuthTest do
|
|||
conn = put_req_header(context.conn, "authorization", authorization)
|
||||
start_supervised!({BasicAuth, context.options})
|
||||
|
||||
assert {_conn, %{payload: %{}}} = BasicAuth.authenticate(@name, conn, [])
|
||||
assert {_conn, %{}} = BasicAuth.authenticate(@name, conn, [])
|
||||
end
|
||||
|
||||
test "returns nil when the username is invalid", context do
|
||||
|
|
|
@ -13,17 +13,17 @@ defmodule LivebookWeb.UserPlugTest do
|
|||
|> fetch_cookies()
|
||||
|> call()
|
||||
|
||||
assert get_session(conn, :current_user_id) != nil
|
||||
assert get_session(conn, :identity_data)[:id] != nil
|
||||
end
|
||||
|
||||
test "keeps user id in the session if present" do
|
||||
conn =
|
||||
conn(:get, "/")
|
||||
|> init_test_session(%{current_user_id: "valid_user_id"})
|
||||
|> init_test_session(%{identity_data: %{id: "valid_user_id"}})
|
||||
|> fetch_cookies()
|
||||
|> call()
|
||||
|
||||
assert get_session(conn, :current_user_id) != nil
|
||||
assert get_session(conn, :identity_data)[:id] != nil
|
||||
end
|
||||
|
||||
test "given no user_data cookie, generates and stores new data" do
|
||||
|
@ -34,10 +34,8 @@ defmodule LivebookWeb.UserPlugTest do
|
|||
|> call()
|
||||
|
||||
assert %{
|
||||
"email" => nil,
|
||||
"hex_color" => <<_::binary>>,
|
||||
"id" => <<_::binary>>,
|
||||
"name" => nil
|
||||
"name" => nil,
|
||||
"hex_color" => <<_::binary>>
|
||||
} = conn.cookies["lb_user_data"] |> Base.decode64!() |> Jason.decode!()
|
||||
end
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue