End-to-end sso domain verification (#5513)

* wip

* Fix up typespcs

* Extract domain status enum to module macros

* Cancel verification jobs when integration is removed

* Revisit verification interface

* Supply local DNS server for domain ownership testing

* Rename a test

* 👾

* Use identifier when submitting domain verification

* Disallow re-verification of already verified domains
This commit is contained in:
Adam Rutkowski 2025-06-18 11:52:48 +02:00 committed by GitHub
parent 9256f4ff9c
commit 37e718db40
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 326 additions and 86 deletions

View File

@ -93,6 +93,14 @@ sso-stop:
docker stop idp
docker remove idp
generate-corefile:
$(call require, integration_id)
integration_id=$(integration_id) envsubst < $(PWD)/extra/fixture/Corefile.template > $(PWD)/extra/fixture/Corefile.gen.$(integration_id)
mock-dns: generate-corefile
$(call require, integration_id)
docker run --rm -p 5353:53/udp -v $(PWD)/extra/fixture/Corefile.gen.$(integration_id):/Corefile coredns/coredns:latest -conf Corefile
loadtest-server:
@echo "Ensure your OTP installation is built with --enable-lock-counter"
MIX_ENV=load ERL_FLAGS="-emu_type lcnt +Mdai max" iex -S mix do phx.digest + phx.server

View File

@ -15,6 +15,7 @@ SHOW_CITIES=true
PADDLE_VENDOR_AUTH_CODE=895e20d4efaec0575bb857f44b183217b332d9592e76e69b8a
PADDLE_VENDOR_ID=3942
SSO_ENABLED=true
SSO_VERIFICATION_NAMESERVERS=0.0.0.0:5353
GOOGLE_CLIENT_ID=875387135161-l8tp53dpt7fdhdg9m1pc3vl42si95rh0.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=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

View File

@ -312,6 +312,23 @@ sso_saml_adapter =
"real" -> PlausibleWeb.SSO.RealSAMLAdapter
end
sso_verification_nameservers =
case get_var_from_path_or_env(config_dir, "SSO_VERIFICATION_NAMESERVERS") do
nil ->
[]
some ->
some
|> String.split(",")
|> Enum.map(fn addr ->
uri = URI.parse("dns://#{addr}")
host = uri.host
port = uri.port || 53
{:ok, addr} = :inet.parse_address(to_charlist(host))
{addr, port}
end)
end
config :plausible,
environment: env,
mailer_email: mailer_email,
@ -323,7 +340,8 @@ config :plausible,
data_dir: data_dir,
session_transfer_dir: session_transfer_dir,
sso_enabled: sso_enabled,
sso_saml_adapter: sso_saml_adapter
sso_saml_adapter: sso_saml_adapter,
sso_verification_nameservers: sso_verification_nameservers
config :plausible, :selfhost,
enable_email_verification: enable_email_verification,

1
extra/fixture/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
Corefile.gen.*

View File

@ -0,0 +1,9 @@
. {
bind 0.0.0.0
template IN TXT plausible.test {
answer "{{ .Name }} 60 IN TXT \"plausible-sso-verification=${integration_id}\""
fallthrough
}
log
errors
}

View File

@ -11,6 +11,8 @@ defmodule Plausible.Auth.SSO do
alias Plausible.Repo
alias Plausible.Teams
use Plausible.Auth.SSO.Domain.Status
@type policy_attr() ::
{:sso_default_role, Teams.Policy.sso_member_role()}
| {:sso_session_timeout_minutes, non_neg_integer()}
@ -184,15 +186,23 @@ defmodule Plausible.Auth.SSO do
case {check, force_deprovision?} do
{:ok, _} ->
Repo.delete!(integration)
{:ok, :ok} =
Repo.transaction(fn ->
integration = Repo.preload(integration, :sso_domains)
Enum.each(integration.sso_domains, &SSO.Domains.cancel_verification(&1.domain))
Repo.delete!(integration)
:ok
end)
:ok
{{:error, :sso_users_present}, true} ->
users = Repo.preload(integration, :users).users
{:ok, :ok} =
Repo.transaction(fn ->
users = Repo.preload(integration, :users).users
integration = Repo.preload(integration, :sso_domains)
Enum.each(users, &deprovision_user!/1)
Enum.each(integration.sso_domains, &SSO.Domains.cancel_verification(&1.domain))
Repo.delete!(integration)
:ok
end)
@ -216,7 +226,7 @@ defmodule Plausible.Auth.SSO do
)
domains = Enum.flat_map(integrations, & &1.sso_domains)
no_verified_domains? = Enum.all?(domains, &(&1.status != :verified))
no_verified_domains? = Enum.all?(domains, &(&1.status != Status.verified()))
cond do
integrations == [] -> {:error, :no_integration}

View File

@ -25,6 +25,8 @@ defmodule Plausible.Auth.SSO.Domain do
@spec verification_methods() :: list(verification_method())
def verification_methods(), do: @verification_methods
use Plausible.Auth.SSO.Domain.Status
schema "sso_domains" do
field :identifier, Ecto.UUID
field :domain, :string
@ -32,8 +34,8 @@ defmodule Plausible.Auth.SSO.Domain do
field :last_verified_at, :naive_datetime
field :status, Ecto.Enum,
values: [:pending, :in_progress, :verified, :unverified],
default: :pending
values: Status.all(),
default: Status.pending()
belongs_to :sso_integration, Plausible.Auth.SSO.Integration
@ -59,11 +61,11 @@ defmodule Plausible.Auth.SSO.Domain do
|> change()
|> put_change(:verified_via, method)
|> put_change(:last_verified_at, now)
|> put_change(:status, :verified)
|> put_change(:status, Status.verified())
end
@spec unverified_changeset(t(), NaiveDateTime.t(), atom()) :: Ecto.Changeset.t()
def unverified_changeset(sso_domain, now, status \\ :in_progress) do
def unverified_changeset(sso_domain, now, status \\ Status.in_progress()) do
sso_domain
|> change()
|> put_change(:verified_via, nil)

View File

@ -0,0 +1,21 @@
defmodule Plausible.Auth.SSO.Domain.Status do
@moduledoc false
defmacro __using__(opts) do
as = Keyword.get(opts, :as, Status)
quote do
require Plausible.Auth.SSO.Domain.Status
alias Plausible.Auth.SSO.Domain.Status, as: unquote(as)
end
end
defmacro pending(), do: :pending
defmacro in_progress(), do: :in_progress
defmacro verified(), do: :verified
defmacro unverified(), do: :unverified
defmacro all() do
[pending(), in_progress(), verified(), unverified()]
end
end

View File

@ -9,29 +9,33 @@ defmodule Plausible.Auth.SSO.Domain.Verification.Worker do
queue: :sso_domain_ownership_verification,
unique: true
use Plausible.Auth.SSO.Domain.Status
alias Plausible.Auth.SSO
alias Plausible.Repo
# roughly around 34h, given the snooze back-off
@max_snoozes 14
@spec cancel(String.t()) :: :ok
def cancel(domain) do
{:ok, job} =
%{domain: domain}
|> new()
|> Oban.insert()
Oban.cancel_job(job)
end
@spec enqueue(String.t()) :: {:ok, Oban.Job.t()}
def enqueue(domain) do
{:ok, result} =
Repo.transaction(fn ->
with {:ok, sso_domain} <- SSO.Domains.get(domain) do
SSO.Domains.mark_unverified!(sso_domain, :in_progress)
end
{:ok, job} =
%{domain: domain}
|> new()
|> Oban.insert()
{:ok, job} =
%{domain: domain}
|> new()
|> Oban.insert()
:ok = Oban.retry_job(job)
{:ok, job}
end)
result
:ok = Oban.retry_job(job)
{:ok, job}
end
@impl true
@ -42,7 +46,10 @@ defmodule Plausible.Auth.SSO.Domain.Verification.Worker do
})
when attempt <= @max_snoozes do
service_opts = [
skip_checks?: meta["skip_checks"] == true
skip_checks?: meta["skip_checks"] == true,
verification_opts: [
nameservers: Application.get_env(:plausible, :sso_verification_nameservers) || []
]
]
service_opts =
@ -55,7 +62,7 @@ defmodule Plausible.Auth.SSO.Domain.Verification.Worker do
case SSO.Domains.get(domain) do
{:ok, sso_domain} ->
case SSO.Domains.verify(sso_domain, service_opts) do
%SSO.Domain{status: :verified} = verified ->
%SSO.Domain{status: Status.verified()} = verified ->
verification_complete(sso_domain)
{:ok, verified}
@ -82,7 +89,7 @@ defmodule Plausible.Auth.SSO.Domain.Verification.Worker do
defp verification_failure(domain) do
with {:ok, sso_domain} <- SSO.Domains.get(domain) do
sso_domain
|> SSO.Domains.mark_unverified!(:unverified)
|> SSO.Domains.mark_unverified!(Status.unverified())
|> send_failure_notification()
end

View File

@ -7,8 +7,11 @@ defmodule Plausible.Auth.SSO.Domains do
alias Plausible.Auth
alias Plausible.Auth.SSO
alias Plausible.Auth.SSO.Domain.Verification
alias Plausible.Repo
use Plausible.Auth.SSO.Domain.Status
@spec add(SSO.Integration.t(), String.t()) ::
{:ok, SSO.Domain.t()} | {:error, Ecto.Changeset.t()}
def add(integration, domain) do
@ -17,6 +20,34 @@ defmodule Plausible.Auth.SSO.Domains do
Repo.insert(changeset)
end
@spec start_verification(String.t()) :: SSO.Domain.t()
def start_verification(domain) when is_binary(domain) do
{:ok, result} =
Repo.transaction(fn ->
with {:ok, sso_domain} <- get(domain) do
sso_domain = mark_unverified!(sso_domain, Status.in_progress())
{:ok, _} = Verification.Worker.enqueue(domain)
{:ok, sso_domain}
end
end)
result
end
@spec cancel_verification(String.t()) :: :ok
def cancel_verification(domain) when is_binary(domain) do
{:ok, :ok} =
Repo.transaction(fn ->
with {:ok, sso_domain} <- get(domain) do
mark_unverified!(sso_domain, Status.unverified())
end
:ok = Verification.Worker.cancel(domain)
end)
:ok
end
@spec verify(SSO.Domain.t(), Keyword.t()) :: SSO.Domain.t()
def verify(%SSO.Domain{} = sso_domain, opts \\ []) do
skip_checks? = Keyword.get(opts, :skip_checks?, false)
@ -69,7 +100,7 @@ defmodule Plausible.Auth.SSO.Domains do
inner_join: i in assoc(d, :sso_integration),
inner_join: t in assoc(i, :team),
where: d.domain == ^search,
where: d.status == :verified,
where: d.status == ^Status.verified(),
preload: [sso_integration: {i, team: t}]
)
|> Repo.one()
@ -90,17 +121,21 @@ defmodule Plausible.Auth.SSO.Domains do
case {check, force_deprovision?} do
{:ok, _} ->
Repo.delete!(sso_domain)
{:ok, :ok} =
Repo.transaction(fn ->
Repo.delete!(sso_domain)
:ok = cancel_verification(sso_domain.domain)
end)
:ok
{{:error, :sso_users_present}, true} ->
domain_users = users_by_domain(sso_domain)
{:ok, :ok} =
Repo.transaction(fn ->
domain_users = users_by_domain(sso_domain)
Enum.each(domain_users, &SSO.deprovision_user!/1)
Repo.delete!(sso_domain)
:ok
cancel_verification(sso_domain.domain)
end)
:ok

View File

@ -8,13 +8,16 @@ defmodule PlausibleWeb.Live.SSOManagement do
alias Plausible.Teams
alias PlausibleWeb.Router.Helpers, as: Routes
use Plausible.Auth.SSO.Domain.Status
@fake_verify_interval :timer.seconds(10)
@refresh_integration_interval :timer.seconds(5)
def mount(_params, _session, socket) do
socket = load_integration(socket, socket.assigns.current_team)
Process.send_after(self(), :fake_domain_verify, @fake_verify_interval)
if connected?(socket) do
Process.send_after(self(), :refresh_integration, @refresh_integration_interval)
end
{:ok, route_mode(socket)}
end
@ -172,7 +175,7 @@ defmodule PlausibleWeb.Live.SSOManagement do
<div class="flex-col space-y-6">
<p class="text-sm">Verifying domain {@domain.domain}</p>
<p class="text-sm">You can verify the domain using one of 3 methods:</p>
<p class="text-sm">You can verify ownership of the domain using one of 3 methods:</p>
<ul class="list-disc ml-4 space-y-6">
<li>
@ -201,8 +204,20 @@ defmodule PlausibleWeb.Live.SSOManagement do
</li>
</ul>
<form id="show-manage" for={} phx-submit="show-manage">
<.button type="submit">Continue</.button>
<.notice>
We'll keep checking your domain ownership. Once any of the above verification methods succeeds, we'll send you an e-mail. Thank you for your patience.
</.notice>
<form id="verify-domain-submit" for={} phx-submit="verify-domain-submit">
<.input type="hidden" name="identifier" value={@domain.identifier} />
<.button
:if={@domain.status in [Status.in_progress(), Status.unverified(), Status.verified()]}
type="submit"
>
Run verification now
</.button>
<.button :if={@domain.status == Status.pending()} type="submit">Continue</.button>
</form>
</div>
"""
@ -296,16 +311,31 @@ defmodule PlausibleWeb.Live.SSOManagement do
<.table rows={@integration.sso_domains}>
<:thead>
<.th>Domain</.th>
<.th>Added at</.th>
<.th hide_on_mobile>Added at</.th>
<.th>Status</.th>
<.th invisible>Actions</.th>
</:thead>
<:tbody :let={domain}>
<.td>{domain.domain}</.td>
<.td>{Calendar.strftime(domain.inserted_at, "%b %-d, %Y at %H:%m UTC")}</.td>
<.td>{domain.status}</.td>
<.td hide_on_mobile>
{Calendar.strftime(domain.inserted_at, "%b %-d, %Y at %H:%m UTC")}
</.td>
<.td :if={domain.status != Status.in_progress()}>{domain.status}</.td>
<.td :if={domain.status == Status.in_progress()}>
<div class="flex items-center gap-x-2">
<.spinner class="w-4 h-4" />
<.styled_link
id={"cancel-verify-domain-#{domain.identifier}"}
phx-click="cancel-verify-domain"
phx-value-identifier={domain.identifier}
>
Cancel
</.styled_link>
</div>
</.td>
<.td actions>
<.styled_link
:if={domain.status not in [Status.in_progress(), Status.verified()]}
id={"verify-domain-#{domain.identifier}"}
phx-click="verify-domain"
phx-value-identifier={domain.identifier}
@ -458,8 +488,16 @@ defmodule PlausibleWeb.Live.SSOManagement do
{:noreply, socket}
end
def handle_event("show-manage", _params, socket) do
{:noreply, route_mode(socket, :manage)}
def handle_event("verify-domain-submit", params, socket) do
integration = socket.assigns.integration
sso_domain = Enum.find(integration.sso_domains, &(&1.identifier == params["identifier"]))
if sso_domain do
SSO.Domains.start_verification(sso_domain.domain)
{:noreply, route_mode(load_integration(socket, socket.assigns.current_team), :manage)}
else
{:noreply, socket}
end
end
def handle_event("show-domain-setup", _params, socket) do
@ -483,6 +521,21 @@ defmodule PlausibleWeb.Live.SSOManagement do
end
end
def handle_event("cancel-verify-domain", params, socket) do
integration = socket.assigns.integration
domain = Enum.find(integration.sso_domains, &(&1.identifier == params["identifier"]))
socket =
if domain do
:ok = SSO.Domains.cancel_verification(domain.domain)
load_integration(socket, socket.assigns.current_team)
else
socket
end
{:noreply, socket}
end
def handle_event("remove-domain", params, socket) do
integration = socket.assigns.integration
domain = Enum.find(integration.sso_domains, &(&1.identifier == params["identifier"]))
@ -551,29 +604,9 @@ defmodule PlausibleWeb.Live.SSOManagement do
{:noreply, socket}
end
def handle_info(:fake_domain_verify, %{assigns: %{integration: integration}} = socket)
when not is_nil(integration) do
sso_domains =
integration.sso_domains
|> Enum.map(fn domain ->
if domain.status == :pending do
SSO.Domains.verify(domain, skip_checks?: true)
else
domain
end
end)
Process.send_after(self(), :fake_domain_verify, @fake_verify_interval)
integration = %{integration | sso_domains: sso_domains}
{:noreply, assign(socket, :integration, integration)}
end
def handle_info(:fake_domain_verify, socket) do
Process.send_after(self(), :fake_domain_verify, @fake_verify_interval)
{:noreply, socket}
def handle_info(:refresh_integration, socket) do
Process.send_after(self(), :refresh_integration, @refresh_integration_interval)
{:noreply, load_integration(socket, socket.assigns.current_team)}
end
defp load_integration(socket, team) do

View File

@ -6,6 +6,8 @@ defmodule Plausible.Auth.SSO.DomainsTest do
on_ee do
use Plausible.Teams.Test
use Plausible.Auth.SSO.Domain.Status
use Oban.Testing, repo: Plausible.Repo
alias Plausible.Auth.SSO
alias Plausible.Teams
@ -29,7 +31,7 @@ defmodule Plausible.Auth.SSO.DomainsTest do
assert is_binary(sso_domain.identifier)
refute sso_domain.verified_via
refute sso_domain.last_verified_at
assert sso_domain.status == :pending
assert sso_domain.status == Status.pending()
end
test "normalizes domain before adding", %{integration: integration} do
@ -110,7 +112,7 @@ defmodule Plausible.Auth.SSO.DomainsTest do
assert verified_domain.id == sso_domain.id
assert verified_domain.verified_via == :dns_txt
assert verified_domain.status == :verified
assert verified_domain.status == Status.verified()
assert verified_domain.last_verified_at
end
@ -124,11 +126,49 @@ defmodule Plausible.Auth.SSO.DomainsTest do
assert unverified_domain.id == sso_domain.id
refute unverified_domain.verified_via
assert unverified_domain.status == :in_progress
assert unverified_domain.status == Status.in_progress()
assert unverified_domain.last_verified_at
end
end
describe "start_verification/1" do
test "no domain" do
assert {:error, :not_found} = SSO.Domains.start_verification("example.com")
end
test "sets domain status to in progress", %{integration: integration} do
domain = generate_domain()
{:ok, _} = SSO.Domains.add(integration, domain)
assert {:ok, sso_domain} = SSO.Domains.start_verification(domain)
assert sso_domain.status == Status.in_progress()
end
test "enqueues background work", %{integration: integration} do
domain = generate_domain()
{:ok, _} = SSO.Domains.add(integration, domain)
assert {:ok, _} = SSO.Domains.start_verification(domain)
assert_enqueued(
worker: Plausible.Auth.SSO.Domain.Verification.Worker,
args: %{domain: domain}
)
end
end
describe "cancel_verification/1" do
test "no domain" do
assert :ok = SSO.Domains.cancel_verification("example.com")
end
test "sets domain status to unverified", %{integration: integration} do
domain = generate_domain()
{:ok, _} = SSO.Domains.add(integration, domain)
assert {:ok, sso_domain} = SSO.Domains.start_verification(domain)
assert :ok = SSO.Domains.cancel_verification(domain)
assert Repo.reload!(sso_domain).status == Status.unverified()
end
end
describe "lookup/1" do
test "looks up domain by email", %{integration: integration} do
domain = generate_domain()

View File

@ -3,6 +3,7 @@ defmodule Plausible.Auth.SSOTest do
use Plausible
on_ee do
use Oban.Testing, repo: Plausible.Repo
use Plausible.Teams.Test
alias Plausible.Auth
@ -746,6 +747,50 @@ defmodule Plausible.Auth.SSOTest do
refute sso_user.sso_identity_id
refute sso_user.sso_integration_id
end
test "cancels verification jobs for all domains when integration is removed" do
team = new_site().team
integration = SSO.initiate_saml_integration(team)
domain1 = "example-#{Enum.random(1..10_000)}.com"
domain2 = "test-#{Enum.random(1..10_000)}.com"
{:ok, _} = SSO.Domains.add(integration, domain1)
{:ok, _} = SSO.Domains.add(integration, domain2)
{:ok, _} = SSO.Domains.start_verification(domain1)
{:ok, _} = SSO.Domains.start_verification(domain2)
assert_enqueued(worker: SSO.Domain.Verification.Worker, args: %{domain: domain1})
assert_enqueued(worker: SSO.Domain.Verification.Worker, args: %{domain: domain2})
assert :ok = SSO.remove_integration(integration)
refute Repo.reload(integration)
refute_enqueued(worker: SSO.Domain.Verification.Worker, args: %{domain: domain1})
refute_enqueued(worker: SSO.Domain.Verification.Worker, args: %{domain: domain2})
end
test "cancels verification jobs when integration is force removed with SSO users" do
team = new_site().team
integration = SSO.initiate_saml_integration(team)
domain = "example-#{Enum.random(1..10_000)}.com"
{:ok, sso_domain} = SSO.Domains.add(integration, domain)
SSO.Domains.verify(sso_domain, skip_checks?: true)
identity = new_identity("Test User", "test@" <> domain)
{:ok, _, _, _} = SSO.provision_user(identity)
{:ok, _} = SSO.Domains.start_verification(domain)
assert_enqueued(worker: SSO.Domain.Verification.Worker, args: %{domain: domain})
assert :ok = SSO.remove_integration(integration, force_deprovision?: true)
refute Repo.reload(integration)
refute_enqueued(worker: SSO.Domain.Verification.Worker, args: %{domain: domain})
end
end
defp new_identity(name, email, id \\ Ecto.UUID.generate()) do

View File

@ -1,5 +1,6 @@
defmodule PlausibleWeb.Live.SSOMangementTest do
use PlausibleWeb.ConnCase, async: false
use Oban.Testing, repo: Plausible.Repo
@moduletag :ee_only
@ -108,7 +109,12 @@ defmodule PlausibleWeb.Live.SSOMangementTest do
text = render(lv) |> text()
assert text =~ "Verifying domain"
lv |> element("form#show-manage") |> render_submit()
lv |> element("form#verify-domain-submit") |> render_submit()
assert_enqueued(
worker: SSO.Domain.Verification.Worker,
args: %{domain: "example.com"}
)
html = render(lv)
text = text(html)
@ -185,7 +191,12 @@ defmodule PlausibleWeb.Live.SSOMangementTest do
html = render(lv)
assert text(html) =~ "Verifying domain new.example.com"
lv |> element("form#show-manage") |> render_submit()
lv |> element("form#verify-domain-submit") |> render_submit()
assert_enqueued(
worker: SSO.Domain.Verification.Worker,
args: %{domain: "new.example.com"}
)
text = text(render(lv))
assert text =~ "org.example.com"

View File

@ -3,15 +3,13 @@ defmodule Plausible.Auth.SSO.Domain.Verification.WorkerTest do
use Plausible
on_ee do
use Oban.Testing, repo: Plausible.Repo
use Bamboo.Test, shared: true
alias Plausible.Auth.SSO.Domain.Verification.Worker
use Oban.Testing, repo: Plausible.Repo
use Plausible.Auth.SSO.Domain.Status
use Plausible.Teams.Test
alias Plausible.Auth.SSO
use Plausible.Teams.Test
alias Plausible.Auth.SSO.Domain.Verification.Worker
test "no sso domain cancels the job" do
assert {:cancel, :domain_not_found} =
@ -29,6 +27,13 @@ defmodule Plausible.Auth.SSO.Domain.Verification.WorkerTest do
assert_enqueued(worker: Worker, args: %{domain: "example.com"})
end
test "enqueue then cancel" do
{:ok, _} = Worker.enqueue("example.com")
assert_enqueued(worker: Worker, args: %{domain: "example.com"})
:ok = Worker.cancel("example.com")
refute_enqueued(worker: Worker, args: %{domain: "example.com"})
end
describe "integration set up" do
setup do
owner = new_user()
@ -45,19 +50,13 @@ defmodule Plausible.Auth.SSO.Domain.Verification.WorkerTest do
sso_domain: sso_domain}
end
test "enqueue resets domain status", %{sso_domain: sso_domain} do
%{status: :unverified} = SSO.Domains.mark_unverified!(sso_domain, :unverified)
{:ok, _} = Worker.enqueue(sso_domain.domain)
assert Plausible.Repo.reload!(sso_domain).status == :in_progress
end
test "domain is marked as in progress and job is snoozed", %{domain: domain} do
assert {:ok, %{status: :pending}} = SSO.Domains.get(domain)
assert {:ok, %{status: Status.pending()}} = SSO.Domains.get(domain)
assert {:snooze, 15} =
perform_job(Worker, %{"domain" => domain}, meta: %{bypass_checks: true})
assert {:ok, %{status: :in_progress}} = SSO.Domains.get(domain)
assert {:ok, %{status: Status.in_progress()}} = SSO.Domains.get(domain)
assert {:snooze, 7680} =
perform_job(Worker, %{"domain" => domain},
@ -65,7 +64,7 @@ defmodule Plausible.Auth.SSO.Domain.Verification.WorkerTest do
meta: %{bypass_checks: true}
)
assert {:ok, %{status: :in_progress}} = SSO.Domains.get(domain)
assert {:ok, %{status: Status.in_progress()}} = SSO.Domains.get(domain)
end
test "domain is marked as verified and emails are sent", %{
@ -75,7 +74,7 @@ defmodule Plausible.Auth.SSO.Domain.Verification.WorkerTest do
} do
owner2 = add_member(team, role: :owner)
assert {:ok, %{status: :verified}} =
assert {:ok, %{status: Status.verified()}} =
perform_job(Worker, %{"domain" => domain}, meta: %{skip_checks: true})
assert_email_delivered_with(