fix(ux): Rename Magic Link to Email (OTP) (#5939)

Fixes #5927 

See https://www.firezone.dev/kb/authenticate/email


<img width="1258" alt="Screenshot 2024-07-21 at 11 29 59 AM"
src="https://github.com/user-attachments/assets/07d5596f-b74c-4bc7-91df-3565ae552f15">
This commit is contained in:
Jamil
2024-07-21 19:42:58 -07:00
committed by GitHub
parent d01afc79f1
commit 286ca77725
20 changed files with 59 additions and 47 deletions

View File

@@ -404,13 +404,13 @@ Interactive Elixir (1.15.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(web@web-3vmw.us-east1-d.c.firezone-staging.internal)1> {:ok, account} = Domain.Accounts.create_account(%{name: "Firezone", slug: "firezone"})
{:ok, ...}
iex(web@web-3vmw.us-east1-d.c.firezone-staging.internal)2> {:ok, magic_link_provider} = Domain.Auth.create_provider(account, %{name: "Email", adapter: :email, adapter_config: %{}})
iex(web@web-3vmw.us-east1-d.c.firezone-staging.internal)2> {:ok, email_provider} = Domain.Auth.create_provider(account, %{name: "Email (OTP)", adapter: :email, adapter_config: %{}})
{:ok, ...}
iex(web@web-3vmw.us-east1-d.c.firezone-staging.internal)3> {:ok, actor} = Domain.Actors.create_actor(account, %{type: :account_admin_user, name: "Andrii Dryga"})
{:ok, ...}
iex(web@web-3vmw.us-east1-d.c.firezone-staging.internal)4> {:ok, identity} = Domain.Auth.upsert_identity(actor, magic_link_provider, %{provider_identifier: "a@firezone.dev", provider_identifier_confirmation: "a@firezone.dev"})
iex(web@web-3vmw.us-east1-d.c.firezone-staging.internal)4> {:ok, identity} = Domain.Auth.upsert_identity(actor, email_provider, %{provider_identifier: "a@firezone.dev", provider_identifier_confirmation: "a@firezone.dev"})
...
iex(web@web-3vmw.us-east1-d.c.firezone-staging.internal)5> context = %Domain.Auth.Context{type: :browser, user_agent: "User-Agent: iOS/12.7 (iPhone) connlib/0.7.412", remote_ip: {127, 0, 0, 1}}

View File

@@ -72,7 +72,7 @@ defmodule Domain.Auth do
alias Domain.Auth.Identity
# This session duration is used when IdP doesn't return the token expiration date,
# or no IdP is used (eg. sign in via magic link or userpass).
# or no IdP is used (eg. sign in via email or userpass).
@default_session_duration_hours [
browser: [
account_admin_user: 10,
@@ -310,7 +310,7 @@ defmodule Domain.Auth do
end)
end
# used during magic link auth flow
# used during email auth flow
def fetch_active_identity_by_provider_and_identifier(
%Provider{adapter: :email} = provider,
provider_identifier,

View File

@@ -267,9 +267,9 @@ defmodule Domain.Billing.EventHandler do
membership_rules: [%{operator: true}]
})
{:ok, magic_link_provider} =
{:ok, email_provider} =
Domain.Auth.create_provider(account, %{
name: "Email",
name: "Email (OTP)",
adapter: :email,
adapter_config: %{}
})
@@ -281,7 +281,7 @@ defmodule Domain.Billing.EventHandler do
})
{:ok, _identity} =
Domain.Auth.upsert_identity(actor, magic_link_provider, %{
Domain.Auth.upsert_identity(actor, email_provider, %{
provider_identifier: metadata["account_admin_email"] || account_email,
provider_identifier_confirmation: metadata["account_admin_email"] || account_email
})

View File

@@ -27,7 +27,7 @@ defmodule Domain.Ops do
membership_rules: [%{operator: true}]
})
{:ok, magic_link_provider} =
{:ok, email_provider} =
Domain.Auth.create_provider(account, %{
name: "Email",
adapter: :email,
@@ -41,12 +41,12 @@ defmodule Domain.Ops do
})
{:ok, identity} =
Domain.Auth.upsert_identity(actor, magic_link_provider, %{
Domain.Auth.upsert_identity(actor, email_provider, %{
provider_identifier: account_admin_email,
provider_identifier_confirmation: account_admin_email
})
%{account: account, provider: magic_link_provider, actor: actor, identity: identity}
%{account: account, provider: email_provider, actor: actor, identity: identity}
end)
end
@@ -76,13 +76,13 @@ defmodule Domain.Ops do
Domain.Repo.transaction(fn ->
{:ok, account} = Domain.Accounts.fetch_account_by_id_or_slug(account_slug)
providers = Domain.Auth.all_active_providers_for_account!(account)
magic_link_provider = Enum.find(providers, fn provider -> provider.adapter == :email end)
email_provider = Enum.find(providers, fn provider -> provider.adapter == :email end)
{:ok, actor} =
Domain.Actors.create_actor(account, %{type: :account_admin_user, name: "Firezone Support"})
{:ok, identity} =
Domain.Auth.upsert_identity(actor, magic_link_provider, %{
Domain.Auth.upsert_identity(actor, email_provider, %{
provider_identifier: "ent-support@firezone.dev",
provider_identifier_confirmation: "ent-support@firezone.dev"
})

View File

@@ -0,0 +1,12 @@
defmodule Domain.Repo.Migrations.RenameMagicLinkToEmailOTP do
use Ecto.Migration
def change do
"""
UPDATE auth_providers
SET name = 'Email (OTP)'
WHERE adapter = 'email';
"""
|> execute("")
end
end

View File

@@ -62,7 +62,7 @@ defmodule Web.AuthController do
@doc """
This is a callback for the Email provider which sends login link.
"""
def request_magic_link(
def request_email_otp(
conn,
%{
"account_id_or_slug" => account_id_or_slug,
@@ -76,7 +76,7 @@ defmodule Web.AuthController do
with true <- String.contains?(provider_identifier, "@"),
{:ok, provider} <- Domain.Auth.fetch_active_provider_by_id(provider_id) do
conn = maybe_send_magic_link_email(conn, provider, provider_identifier, redirect_params)
conn = maybe_send_email_otp(conn, provider, provider_identifier, redirect_params)
signed_provider_identifier =
Plug.Crypto.sign(
@@ -110,7 +110,7 @@ defmodule Web.AuthController do
end
end
defp maybe_send_magic_link_email(conn, provider, provider_identifier, redirect_params) do
defp maybe_send_email_otp(conn, provider, provider_identifier, redirect_params) do
context_type = Web.Auth.fetch_auth_context_type!(redirect_params)
context = Web.Auth.get_auth_context(conn, context_type)
@@ -125,7 +125,7 @@ defmodule Web.AuthController do
),
{:ok, identity} <-
Domain.Auth.Adapters.Email.request_sign_in_token(identity, context),
{:ok, fragment} <- send_magic_link_email(conn, identity, redirect_params) do
{:ok, fragment} <- send_email_otp(conn, identity, redirect_params) do
fragment
else
_ ->
@@ -144,7 +144,7 @@ defmodule Web.AuthController do
put_auth_state(conn, provider.id, {fragment, provider_identifier, redirect_params})
end
defp send_magic_link_email(conn, identity, redirect_params) do
defp send_email_otp(conn, identity, redirect_params) do
# Nonce is the short part that is sent to the user in the email
nonce = identity.provider_virtual_state.nonce

View File

@@ -38,7 +38,7 @@ defmodule Web.Settings.IdentityProviders.GoogleWorkspace.Show do
<.section>
<:title>
Identity Provider <code><%= @provider.name %></code>
Identity Provider: <code><%= @provider.name %></code>
<span :if={not is_nil(@provider.disabled_at)} class="text-primary-600">(disabled)</span>
<span :if={not is_nil(@provider.deleted_at)} class="text-red-600">(deleted)</span>
</:title>

View File

@@ -41,7 +41,7 @@ defmodule Web.Settings.IdentityProviders.JumpCloud.Show do
<.section>
<:title>
Identity Provider <code><%= @provider.name %></code>
Identity Provider: <code><%= @provider.name %></code>
<span :if={not is_nil(@provider.disabled_at)} class="text-primary-600">(disabled)</span>
<span :if={not is_nil(@provider.deleted_at)} class="text-red-600">(deleted)</span>
</:title>

View File

@@ -38,7 +38,7 @@ defmodule Web.Settings.IdentityProviders.MicrosoftEntra.Show do
<.section>
<:title>
Identity Provider <code><%= @provider.name %></code>
Identity Provider: <code><%= @provider.name %></code>
<span :if={not is_nil(@provider.disabled_at)} class="text-primary-600">(disabled)</span>
<span :if={not is_nil(@provider.deleted_at)} class="text-red-600">(deleted)</span>
</:title>

View File

@@ -38,7 +38,7 @@ defmodule Web.Settings.IdentityProviders.Okta.Show do
<.section>
<:title>
Identity Provider <code><%= @provider.name %></code>
Identity Provider: <code><%= @provider.name %></code>
<span :if={not is_nil(@provider.disabled_at)} class="text-primary-600">(disabled)</span>
<span :if={not is_nil(@provider.deleted_at)} class="text-red-600">(deleted)</span>
</:title>

View File

@@ -33,7 +33,7 @@ defmodule Web.Settings.IdentityProviders.OpenIDConnect.Show do
<.section>
<:title>
Identity Provider <code><%= @provider.name %></code>
Identity Provider: <code><%= @provider.name %></code>
<span :if={not is_nil(@provider.disabled_at)} class="text-primary-600">(disabled)</span>
<span :if={not is_nil(@provider.deleted_at)} class="text-red-600">(deleted)</span>
</:title>

View File

@@ -33,7 +33,7 @@ defmodule Web.Settings.IdentityProviders.System.Show do
<.section>
<:title>
Identity Provider <code><%= @provider.name %></code>
Identity Provider: <code><%= @provider.name %></code>
<span :if={not is_nil(@provider.disabled_at)} class="text-primary-600">(disabled)</span>
<span :if={not is_nil(@provider.deleted_at)} class="text-red-600">(deleted)</span>
</:title>

View File

@@ -204,7 +204,7 @@ defmodule Web.SignIn do
~H"""
<.form
for={@email_form}
action={~p"/#{@account}/sign_in/providers/#{@provider.id}/request_magic_link"}
action={~p"/#{@account}/sign_in/providers/#{@provider.id}/request_email_otp"}
class="space-y-4 lg:space-y-6"
id="email_form"
phx-update="ignore"

View File

@@ -158,7 +158,7 @@ defmodule Web.SignIn.Email do
as={:email}
class="inline"
action={
~p"/#{@account_id_or_slug}/sign_in/providers/#{@provider_id}/request_magic_link?resend=true"
~p"/#{@account_id_or_slug}/sign_in/providers/#{@provider_id}/request_email_otp?resend=true"
}
method="post"
>

View File

@@ -180,7 +180,7 @@ defmodule Web.SignUp do
id="resend-email"
as={:email}
class="inline"
action={~p"/#{@account}/sign_in/providers/#{@provider}/request_magic_link"}
action={~p"/#{@account}/sign_in/providers/#{@provider}/request_email_otp"}
method="post"
>
<.input

View File

@@ -93,7 +93,7 @@ defmodule Web.Router do
post "/verify_credentials", AuthController, :verify_credentials
# Email
post "/request_magic_link", AuthController, :request_magic_link
post "/request_email_otp", AuthController, :request_email_otp
get "/verify_sign_in_token", AuthController, :verify_sign_in_token
# IdP

View File

@@ -75,7 +75,7 @@ defmodule Web.ConnCase do
|> Plug.Conn.assign(:subject, subject)
end
def put_magic_link_auth_state(
def put_email_auth_state(
conn,
account,
%{adapter: :email} = provider,
@@ -86,7 +86,7 @@ defmodule Web.ConnCase do
Map.merge(%{"email" => %{"provider_identifier" => identity.provider_identifier}}, params)
redirected_conn =
post(conn, ~p"/#{account}/sign_in/providers/#{provider.id}/request_magic_link", params)
post(conn, ~p"/#{account}/sign_in/providers/#{provider.id}/request_email_otp", params)
assert_received {:email, email}
[_match, secret] = Regex.run(~r/secret=([^&\n]*)/, email.text_body)
@@ -136,7 +136,7 @@ defmodule Web.ConnCase do
)
redirected_conn =
post(conn, ~p"/#{account}/sign_in/providers/#{provider.id}/request_magic_link", params)
post(conn, ~p"/#{account}/sign_in/providers/#{provider.id}/request_email_otp", params)
assert_received {:email, email}
[_match, secret] = Regex.run(~r/secret=([^&\n]*)/, email.text_body)

View File

@@ -323,7 +323,7 @@ defmodule Web.AuthControllerTest do
end
end
describe "request_magic_link/2" do
describe "request_email_otp/2" do
test "sends a login link to the user email", %{conn: conn} do
account = Fixtures.Accounts.create_account()
provider = Fixtures.Auth.create_email_provider(account: account)
@@ -332,7 +332,7 @@ defmodule Web.AuthControllerTest do
conn =
post(
conn,
~p"/#{provider.account_id}/sign_in/providers/#{provider.id}/request_magic_link",
~p"/#{provider.account_id}/sign_in/providers/#{provider.id}/request_email_otp",
%{
"email" => %{
"provider_identifier" => identity.provider_identifier
@@ -362,7 +362,7 @@ defmodule Web.AuthControllerTest do
identity = Fixtures.Auth.create_identity(account: account, provider: provider)
for _ <- 1..3 do
post(conn, ~p"/#{account}/sign_in/providers/#{provider}/request_magic_link", %{
post(conn, ~p"/#{account}/sign_in/providers/#{provider}/request_email_otp", %{
"email" => %{
"provider_identifier" => identity.provider_identifier
}
@@ -373,7 +373,7 @@ defmodule Web.AuthControllerTest do
end)
end
post(conn, ~p"/#{account}/sign_in/providers/#{provider}/request_magic_link", %{
post(conn, ~p"/#{account}/sign_in/providers/#{provider}/request_email_otp", %{
"email" => %{
"provider_identifier" => identity.provider_identifier
}
@@ -390,7 +390,7 @@ defmodule Web.AuthControllerTest do
conn =
post(
conn,
~p"/#{provider.account_id}/sign_in/providers/#{provider.id}/request_magic_link",
~p"/#{provider.account_id}/sign_in/providers/#{provider.id}/request_email_otp",
%{
"as" => "client",
"nonce" => "NONCE",
@@ -425,7 +425,7 @@ defmodule Web.AuthControllerTest do
conn =
post(
conn,
~p"/#{account.id}/sign_in/providers/#{provider_id}/request_magic_link",
~p"/#{account.id}/sign_in/providers/#{provider_id}/request_email_otp",
%{"email" => %{"provider_identifier" => "foo@bar.com"}}
)
@@ -440,7 +440,7 @@ defmodule Web.AuthControllerTest do
conn =
post(
conn,
~p"/#{account.id}/sign_in/providers/#{provider_id}/request_magic_link",
~p"/#{account.id}/sign_in/providers/#{provider_id}/request_email_otp",
%{"email" => %{"provider_identifier" => "foo"}}
)
@@ -455,7 +455,7 @@ defmodule Web.AuthControllerTest do
conn =
post(
conn,
~p"/#{account.id}/sign_in/providers/#{provider.id}/request_magic_link",
~p"/#{account.id}/sign_in/providers/#{provider.id}/request_email_otp",
%{"email" => %{"provider_identifier" => "foo@bar"}}
)
@@ -480,7 +480,7 @@ defmodule Web.AuthControllerTest do
actor = Fixtures.Actors.create_actor(type: :account_admin_user, account: account)
identity = Fixtures.Auth.create_identity(account: account, provider: provider, actor: actor)
{conn_with_cookie, secret} = put_magic_link_auth_state(conn, account, provider, identity)
{conn_with_cookie, secret} = put_email_auth_state(conn, account, provider, identity)
%{
account: account,
@@ -596,7 +596,7 @@ defmodule Web.AuthControllerTest do
}
{conn_with_cookie, _secret} =
put_magic_link_auth_state(conn, account, provider, identity, redirect_params)
put_email_auth_state(conn, account, provider, identity, redirect_params)
conn =
conn_with_cookie
@@ -679,7 +679,7 @@ defmodule Web.AuthControllerTest do
}
{conn_with_cookie, secret} =
put_magic_link_auth_state(conn, account, provider, identity, redirect_params)
put_email_auth_state(conn, account, provider, identity, redirect_params)
conn =
conn_with_cookie
@@ -1233,7 +1233,7 @@ defmodule Web.AuthControllerTest do
provider: provider
)
{conn, secret} = put_magic_link_auth_state(conn, account, provider, identity)
{conn, secret} = put_email_auth_state(conn, account, provider, identity)
authorized_conn =
conn

View File

@@ -27,7 +27,7 @@ defmodule Web.SignIn.EmailTest do
identity: identity,
conn: conn
} do
{conn, _secret} = put_magic_link_auth_state(conn, account, provider, identity)
{conn, _secret} = put_email_auth_state(conn, account, provider, identity)
signed_provider_identifier =
Plug.Crypto.sign(
@@ -53,7 +53,7 @@ defmodule Web.SignIn.EmailTest do
identity: identity,
conn: conn
} do
{conn, secret} = put_magic_link_auth_state(conn, account, provider, identity)
{conn, secret} = put_email_auth_state(conn, account, provider, identity)
signed_provider_identifier =
Plug.Crypto.sign(
@@ -99,7 +99,7 @@ defmodule Web.SignIn.EmailTest do
"redirect_to" => "/foo"
}
{conn, secret} = put_magic_link_auth_state(conn, account, provider, identity, redirect_params)
{conn, secret} = put_email_auth_state(conn, account, provider, identity, redirect_params)
signed_provider_identifier =
Plug.Crypto.sign(
@@ -139,7 +139,7 @@ defmodule Web.SignIn.EmailTest do
identity: identity,
conn: conn
} do
{conn, _secret} = put_magic_link_auth_state(conn, account, provider, identity)
{conn, _secret} = put_email_auth_state(conn, account, provider, identity)
signed_provider_identifier =
Plug.Crypto.sign(

View File

@@ -714,7 +714,7 @@ export default function Page() {
<li className="flex space-x-2.5">
<HiCheck className="flex-shrink-0 w-5 h-5 text-neutral-900" />
<span className="leading-tight text-lg text-neutral-900 ">
Authenticate with Magic link or OIDC
Authenticate with Email OTP or OIDC
</span>
</li>
<li className="flex space-x-2.5">