The ways into an account a site's admin — or a master, for any site or every site — can close:
:sign_in, :register, :reset (forgot password), :password (signing in or registering with
a password) and :magic_link (sign-in links). Set on the settings page's Security tab, the last
two as switches in its "Sign-in methods" table; see definitions/0.
Who still gets in
A master (site_id nil) always. Anyone else passes a closed scope only by its own "still
allowed" lists: an address on auth.allowed_emails, or a permission on auth.allowed_permissions
(* by default) that the account holds. The installation's switch closes every site, with the
installation's lists; a site's switch closes that site, with the site's lists. Both are asked, so
either can refuse.
Where it is asked
At the account actions, so the dashboard, the sign-in pages and the API all hear the same answer,
refused with MishkaCmsCore.Accounts.AuthGate.Closed:
- registering and completing a reset — policies on
MishkaCmsCore.Accounts.Userbefore the AshAuthentication bypass (MishkaCmsCore.Accounts.Checks.AuthGateOpen), whoseerror_messageisrefused/3, the way AshAuthentication's own guide asks for policies on its actions; - signing in by password, sign-in link or sign-in token — after the password or the token is
checked (
prepare/3), so only someone who knew it learns that sign-in is closed, and they read that rather than "Incorrect email or password"; the token AshAuthentication had just made for them is revoked; - asking for a reset or a sign-in link — never refused, the answer is the same for every address, but the email goes only to an account the gate lets through.
Who is already signed in
Closing sign-in leaves them signed in, unless the same scope also turns on
auth.sign_in.sign_out: then whoever the gate would not let sign in now is signed out
everywhere (signs_out?/2, sign_out/2) at their next request — a page
(MishkaCmsCore.Runtime.Plugs.SiteLookupPlug.confirm_tenant/2), an API call or a GraphQL socket
(MishkaCmsCore.Runtime.Plugs.ApiTenantLookupPlug.confirm_bearer_tenant/2) — and a page already
open reloads on its next navigation or click, or as soon as the switch is saved
(MishkaCmsCore.Runtime.SiteAvailability).
The site being offline
Another question, asked on its own (MishkaCmsCore.Runtime.SiteAvailability): a visitor gets in
only when neither refuses. Taking a site offline signs nobody out.
Plugins
"auth:gate:check" hears every decision before it is final: %{action:, site_id:, user:, email:, allowed?:}, and the allowed? it hands back is the answer — a captcha or an IP list can close a
way the settings left open, or open one they closed. While its plugins cannot run, its
cannot_run: answer closes every way in: what they would close cannot be told.
"auth:gate:refused" hears each refusal
(return: false), and "auth:gate:signed_out" each account sign_out/2 signs out, with
%{site_id:, user_id:, email:} (return: false).
Summary
Types
Values about to be saved, by scope (nil for Global) and key, read before the saved ones.
Functions
The ways in a gate can close.
Whether who — %{user: account} once the account is known, %{email: address} before — may
take action on site_id (nil for a master's sign-in), with pending read over the saved
settings.
:ok, or the refusal for the first of actions the gate keeps who out of — "auth:gate:refused"
told about it.
Whether action is closed on site_id at all — by the installation or the site — whoever asks.
What a page says before anyone is known.
The Security tab: what can close, and who still gets in.
What to tell whoever was refused, in their language.
Whether saving pending would shut user out of site_id when they are not shut out now: sign
them out, or leave them unable to sign in again. The settings page refuses such a save, so an
admin cannot close the way in behind themselves; what someone else's switch already does to them
is not theirs to undo, and is not asked.
The read preparation that refuses a sign-in through a closed gate, once the account is found and
its password or token checked: prepare &AuthGate.prepare(&1, &2, [:sign_in]).
The refusal anywhere inside reason, or nil: an error or error class, a form's raw_errors, or
the JSON:API errors document written for one — the shape of
MishkaCmsCore.Accounts.AuthRateLimit.limited/1.
A gate policy's error_message: the refusal for action, told to "auth:gate:refused" first.
Signs user out on every device with AshAuthentication's log_out_everywhere, which revokes every
token it holds for them, and cuts their open pages.
Whether user, signed in on site_id, is to be signed out: a scope that closed sign-in there also
signs out who is in, and the gate would not let user sign in now. Read from the settings cache
and the permissions user carries, so a cached viewer
(MishkaCmsCore.Runtime.States.ViewerState) is asked as an account is. pending is read over the
saved settings.
Who an action's changeset is for: the address being registered, or the account being reset.
Types
@type action() :: :sign_in | :register | :reset | :password | :magic_link
Values about to be saved, by scope (nil for Global) and key, read before the saved ones.
Functions
@spec actions() :: [action()]
The ways in a gate can close.
Whether who — %{user: account} once the account is known, %{email: address} before — may
take action on site_id (nil for a master's sign-in), with pending read over the saved
settings.
@spec check([action()], String.t() | nil, who()) :: :ok | {:error, MishkaCmsCore.Accounts.AuthGate.Closed.t()}
:ok, or the refusal for the first of actions the gate keeps who out of — "auth:gate:refused"
told about it.
Whether action is closed on site_id at all — by the installation or the site — whoever asks.
What a page says before anyone is known.
@spec definitions() :: [MishkaCmsCore.Settings.definition()]
The Security tab: what can close, and who still gets in.
What to tell whoever was refused, in their language.
Whether saving pending would shut user out of site_id when they are not shut out now: sign
them out, or leave them unable to sign in again. The settings page refuses such a save, so an
admin cannot close the way in behind themselves; what someone else's switch already does to them
is not theirs to undo, and is not asked.
@spec prepare(Ash.Query.t(), map(), [action()]) :: Ash.Query.t()
The read preparation that refuses a sign-in through a closed gate, once the account is found and
its password or token checked: prepare &AuthGate.prepare(&1, &2, [:sign_in]).
The refusal anywhere inside reason, or nil: an error or error class, a form's raw_errors, or
the JSON:API errors document written for one — the shape of
MishkaCmsCore.Accounts.AuthRateLimit.limited/1.
@spec refused(term(), map(), action()) :: MishkaCmsCore.Accounts.AuthGate.Closed.t()
A gate policy's error_message: the refusal for action, told to "auth:gate:refused" first.
error_message &MishkaCmsCore.Accounts.AuthGate.refused(&1, &2, :register)
@spec sign_out(Ash.Resource.Record.t(), String.t() | nil) :: :ok
Signs user out on every device with AshAuthentication's log_out_everywhere, which revokes every
token it holds for them, and cuts their open pages.
Whether user, signed in on site_id, is to be signed out: a scope that closed sign-in there also
signs out who is in, and the gate would not let user sign in now. Read from the settings cache
and the permissions user carries, so a cached viewer
(MishkaCmsCore.Runtime.States.ViewerState) is asked as an account is. pending is read over the
saved settings.
Who an action's changeset is for: the address being registered, or the account being reset.