diff --git a/elixir/apps/web/lib/web/live/auth/email.ex b/elixir/apps/web/lib/web/live/auth/email.ex index 932557cd6..39ccbc253 100644 --- a/elixir/apps/web/lib/web/live/auth/email.ex +++ b/elixir/apps/web/lib/web/live/auth/email.ex @@ -7,7 +7,7 @@ defmodule Web.Auth.Email do "provider_id" => provider_id, "provider_identifier" => provider_identifier } = params, - _session, + session, socket ) do form = to_form(%{"secret" => nil}) @@ -19,7 +19,8 @@ defmodule Web.Auth.Email do account_id_or_slug: account_id_or_slug, provider_id: provider_id, resent: params["resent"], - client_platform: params["client_platform"] + client_platform: session["client_platform"] || params["client_platform"], + client_csrf_token: session["client_csrf_token"] || params["client_csrf_token"] ]} end @@ -50,6 +51,7 @@ defmodule Web.Auth.Email do provider_id={@provider_id} provider_identifier={@provider_identifier} client_platform={@client_platform} + client_csrf_token={@client_csrf_token} />
<.dev_email_provider_link url="https://mail.google.com/mail/" name="Gmail" /> @@ -64,6 +66,7 @@ defmodule Web.Auth.Email do

@@ -126,6 +130,7 @@ defmodule Web.Auth.Email do ~H""" <.form for={%{}} + id="resend-email" as={:email} class="inline" action={ @@ -137,15 +142,24 @@ defmodule Web.Auth.Email do <.input :if={not is_nil(@client_platform)} type="hidden" - name="email[client_platform]" + name="client_platform" value={@client_platform} - /> Did not receive it? - + /> + <.input + :if={not is_nil(@client_csrf_token)} + type="hidden" + name="client_csrf_token" + value={@client_csrf_token} + /> + + Did not receive it? + + """ end diff --git a/elixir/apps/web/test/web/controllers/auth_controller_test.exs b/elixir/apps/web/test/web/controllers/auth_controller_test.exs index 75f7d73cf..1105048d4 100644 --- a/elixir/apps/web/test/web/controllers/auth_controller_test.exs +++ b/elixir/apps/web/test/web/controllers/auth_controller_test.exs @@ -1292,9 +1292,9 @@ defmodule Web.AuthControllerTest do end defp split_token(identity, size \\ 5) do - <> = + <> = identity.provider_virtual_state.sign_in_token - {email_secret, browser_secret} + {email_secret, sign_in_nonce} end end diff --git a/elixir/apps/web/test/web/live/auth/email_test.exs b/elixir/apps/web/test/web/live/auth/email_test.exs index f015c8e60..33afb56b6 100644 --- a/elixir/apps/web/test/web/live/auth/email_test.exs +++ b/elixir/apps/web/test/web/live/auth/email_test.exs @@ -1,12 +1,30 @@ defmodule Web.Auth.EmailTest do use Web.ConnCase, async: true - test "renders email page", %{conn: conn} do + setup do Domain.Config.put_system_env_override(:outbound_email_adapter, Swoosh.Adapters.Postmark) account = Fixtures.Accounts.create_account() provider = Fixtures.Auth.create_email_provider(account: account) + actor = Fixtures.Actors.create_actor(account: account, type: :account_user) + {:ok, identity} = + Fixtures.Auth.create_identity(account: account, actor: actor, provider: provider) + |> Domain.Auth.Adapters.Email.request_sign_in_token() + + %{ + account: account, + provider: provider, + actor: actor, + identity: identity + } + end + + test "renders delivery confirmation page for browser users", %{ + account: account, + provider: provider, + conn: conn + } do {:ok, lv, html} = live(conn, ~p"/#{account}/sign_in/providers/email/#{provider}?provider_identifier=foo") @@ -14,4 +32,160 @@ defmodule Web.Auth.EmailTest do assert has_element?(lv, ~s|a[href="https://mail.google.com/mail/"]|, "Open Gmail") assert has_element?(lv, ~s|a[href="https://outlook.live.com/mail/"]|, "Open Outlook") end + + test "renders token form for Apple users", %{ + account: account, + provider: provider, + identity: identity, + conn: conn + } do + {:ok, lv, _html} = + live( + conn, + ~p"/#{account}/sign_in/providers/email/#{provider}?provider_identifier=foo&client_platform=apple" + ) + + assert has_element?(lv, ~s|form#verify-sign-in-token|) + assert has_element?(lv, "button", "Submit") + + <> = identity.provider_virtual_state.sign_in_token + + conn = + conn + |> put_session(:sign_in_nonce, nonce) + |> put_session(:client_platform, "apple") + |> put_session(:client_csrf_token, "foo") + + conn = + lv + |> form("#verify-sign-in-token", %{ + identity_id: identity.id, + secret: secret + }) + |> submit_form(conn) + + assert redirected_to(conn, 302) =~ "firezone://handle_client_auth_callback" + refute conn.assigns.flash["error"] + end + + test "renders token form for Android users", %{ + account: account, + provider: provider, + identity: identity, + conn: conn + } do + {:ok, lv, _html} = + live( + conn, + ~p"/#{account}/sign_in/providers/email/#{provider}?provider_identifier=foo&client_platform=android" + ) + + <> = identity.provider_virtual_state.sign_in_token + + conn = + conn + |> put_session(:sign_in_nonce, nonce) + |> put_session(:client_platform, "android") + |> put_session(:client_csrf_token, "foo") + + conn = + lv + |> form("#verify-sign-in-token", %{ + identity_id: identity.id, + secret: secret + }) + |> submit_form(conn) + + assert "/handle_client_auth_callback" <> _ = redirected_to(conn, 302) + refute conn.assigns.flash["error"] + end + + test "renders error on invalid token", %{ + account: account, + provider: provider, + identity: identity, + conn: conn + } do + {:ok, lv, _html} = + live( + conn, + ~p"/#{account}/sign_in/providers/email/#{provider}?provider_identifier=foo&client_platform=android" + ) + + <<_secret::binary-size(5), nonce::binary>> = identity.provider_virtual_state.sign_in_token + + conn = + conn + |> put_session(:sign_in_nonce, nonce) + |> put_session(:client_platform, "android") + |> put_session(:client_csrf_token, "foo") + + conn = + lv + |> form("#verify-sign-in-token", %{ + identity_id: identity.id, + secret: "foo" + }) + |> submit_form(conn) + + assert redirected_to(conn, 302) == ~p"/#{account}" + assert conn.assigns.flash["error"] == "The sign in link is invalid or expired." + end + + test "allows to resend magic link", %{ + account: account, + provider: provider, + identity: identity, + conn: conn + } do + {:ok, lv, _html} = + live(conn, ~p"/#{account}/sign_in/providers/email/#{provider}?provider_identifier=foo") + + assert has_element?(lv, ~s|button[type="submit"]|, "Resend email") + + conn = + lv + |> form("#resend-email", %{email: %{provider_identifier: identity.provider_identifier}}) + |> submit_form(conn) + + assert conn.assigns.flash["info"] == "Email was resent." + + assert redirected_to = redirected_to(conn, 302) + assert redirected_to =~ ~p"/#{account}/sign_in/providers/email/#{provider}" + assert redirected_to =~ "provider_identifier=" + refute get_session(conn, :client_platform) + end + + test "does not loose client platform param on email resend", %{ + account: account, + provider: provider, + identity: identity, + conn: conn + } do + conn = put_session(conn, :client_platform, "apple") + + {:ok, lv, _html} = + live( + conn, + ~p"/#{account}/sign_in/providers/email/#{provider}?provider_identifier=foo&client_platform=apple" + ) + + assert has_element?(lv, ~s|button[type="submit"]|, "Resend email") + + conn = + lv + |> form("#resend-email", %{ + email: %{provider_identifier: identity.provider_identifier}, + client_platform: "apple" + }) + |> submit_form(conn) + + assert conn.assigns.flash["info"] == "Email was resent." + + assert redirected_to = redirected_to(conn, 302) + assert redirected_to =~ ~p"/#{account}/sign_in/providers/email/#{provider}" + assert redirected_to =~ "provider_identifier=" + assert redirected_to =~ "client_platform=apple" + assert get_session(conn, :client_platform) == "apple" + end end