pub struct Auth<U = AdminUser> { /* private fields */ }Expand description
The panel’s authentication configuration (ADR-0013).
The default is Auth::password; Auth::custom installs an app-owned
Authenticator; Auth::disabled is the explicit fail-open opt-out.
Sign-in attempts are throttled per login and address by default; see Auth::throttle.
Auth::account_throttle adds an opt-in count per login across addresses.
Auth::registration adds a sign-up page, Auth::profile a profile page, and
Auth::password_reset a forgot-password page that mails reset links through
Auth::mail, Auth::tenant_registration a page creating a tenant, and
Auth::tenant_profile a page editing one.
U is the user type the authenticator loads. Every seam that reads or writes a user, from the
registrar to the tenant editor, names the same type, so a seam over another user type does not
compile.
Implementations§
Source§impl<U: PanelUser> Auth<U>
impl<U: PanelUser> Auth<U>
Sourcepub fn custom<A: Authenticator<User = U>>(authenticator: A) -> Self
pub fn custom<A: Authenticator<User = U>>(authenticator: A) -> Self
An app-owned authenticator over its own user table.
Sourcepub fn throttle(self, throttle: LoginThrottle) -> Self
pub fn throttle(self, throttle: LoginThrottle) -> Self
How many sign-in attempts one login may make from one client address
in a window; defaults to five a minute. LoginThrottle::off counts nothing, for a proxy
that limits sign-ins itself.
Sourcepub fn account_throttle(self, throttle: LoginThrottle) -> Self
pub fn account_throttle(self, throttle: LoginThrottle) -> Self
How many sign-in attempts one login may make from all addresses together in a window;
off by default. It stops a guesser who rotates addresses, at the cost of letting anyone
who knows a login refuse its owner until the window ends, again each window; a successful
sign-in clears it. Turn it on where a guessed password costs more than a locked-out
owner, with a limit high enough that only a distributed guesser reaches it, such as
LoginThrottle::new(50, Duration::from_hours(1)).
Sourcepub fn registration(self, registrar: impl Registrar<User = U>) -> Self
pub fn registration(self, registrar: impl Registrar<User = U>) -> Self
Lets visitors create their own account at {prefix}/register, through registrar; the
login page links to it.
use tablo_core::{Auth, PasswordSignUp};
let auth = Auth::password().registration(PasswordSignUp::pending());PasswordSignUp registers an AdminUser from
a SignUp, inactive until an administrator approves it or, when built
with open, active and signed in at once. A custom Registrar decides what a new
account may do; one whose user may not access the panel lands on the login page.
- The form is any
FormInput, or the shippedSignUp.#[form(password)]renders a masked control, with a button that shows what was typed, that a refused form renders empty, and reads the password as typed, outer spaces included, as the login compares it.#[form(email)]refuses a malformed address beforevalidateruns. A missing field and a malformed value answer 422 with the error under the field, asvalidate’s refusals do; nothing is written. new_password_errorsrefuses a password under eight characters and a confirmation that differs, under thepasswordandpassword_confirmationkeys.- After the commit, an account that may open the panel is signed in, its session rotated as
a login rotates it, and lands on
nextor the panel’s home page. One that may not, such as an account awaiting approval, lands on the login page with a notification instead. A signed-in user who opens the sign-up page lands on the panel’s home page. - The registrar’s
UserisU. Mounting refuses a registration onAuth::disabled(), a form field namedcsrf_tokenornext, a file field and a relationship choice, whose options would list records to a visitor nobody signed in. - Throttling. Each client address may submit five sign-ups a minute, counted whether or not
they create an account;
Auth::sign_up_throttlechanges the limit. Past it the page answers 429.
The sign-up page tells a visitor whether an email is taken, which the login page never does: an app that must keep its accounts private verifies the address by email before saying so, which Tablo does not ship.
Sourcepub fn sign_up_throttle(self, throttle: LoginThrottle) -> Self
pub fn sign_up_throttle(self, throttle: LoginThrottle) -> Self
How many sign-ups one client address may submit in a window; defaults to five a minute. Every sign-up POST that passes the CSRF check and parses counts, whether or not it creates an account.
Sourcepub fn profile(self, editor: impl ProfileEditor<User = U>) -> Self
pub fn profile(self, editor: impl ProfileEditor<User = U>) -> Self
Lets the signed-in user edit their own account at {prefix}/profile, through editor;
the user menu links to it.
use tablo_core::{Auth, PasswordAuth};
let auth = Auth::password().profile(PasswordAuth);PasswordAuth edits an AdminUser’s name, email and password through
a Profile, asking for the current password before the email or the
password changes.
- The form is any
FormInput, rendered in the panel’s shell. The page shows what the input’swritewrites, soProfilenever shows a password. A disabled control keeps whatfillwrote, whatever the POST carries. A refused save renders the form again with each error under its field; nothing is written. saveanswersProfileUpdate::Details, which keeps every session, orProfileUpdate::Credentialsfor a change to what the user signs in with. The user’s sessions then end on every panel whose authenticator loads the same user type, in the transaction that saves, and the browser that saved gets a new session under the tenant it acted for.PasswordAuthanswersCredentialsfor a new email or password.- An email another account holds is reported only once the current password verifies, so a stolen session cannot probe which addresses have accounts.
- After the commit the page reloads with a notification.
- The editor’s
UserisU. Mounting refuses a profile onAuth::disabled(), a form field namedcsrf_token, a file field and a relationship choice, whose options no route serves the page. - Throttling. Each user may submit five saves a minute, from any address, counted whether or
not they save;
Auth::profile_throttlechanges the limit. Past it the page answers 429, which keeps a stolen session from guessing the current password. A user past the limit can still sign in.
Sourcepub fn profile_throttle(self, throttle: LoginThrottle) -> Self
pub fn profile_throttle(self, throttle: LoginThrottle) -> Self
How many profile saves one user may submit in a window, from any address; defaults to five a minute. Every profile POST that passes the CSRF check and parses counts, whether or not it saves.
Sourcepub fn password_reset(self, reset: PasswordReset<U>) -> Self
pub fn password_reset(self, reset: PasswordReset<U>) -> Self
Lets a visitor who forgot their password ask for a reset link at
{prefix}/forgot-password, mailed through Auth::mail, and choose a new password at
{prefix}/reset-password; the login page links to it.
- The request page answers the same whether an account uses the address or not: it redirects to the login page with a notification, and writes and mails the link after answering, so the database and the mail server add no time when an account exists. An account that may not enter the panel gets no link, and a failed send is logged, not shown.
- The link goes to the stored address, never the typed one. A lookup that matches loosely, such as a collation that folds accents, would otherwise mail one account’s link to another address.
- A link works once, for one hour, on the panel that mailed it, and only while the account may still enter the panel. A new request replaces the account’s earlier link, and a password change on the profile page ends it, as it ends every link the account holds. The token is stored only as a hash, and the reset page sends no referrer and may not be cached.
- A reset checks the new password with
new_password_errors, then sets it, ends the account’s sessions and reset links on every panel whose authenticator loads the same user type, and redirects to the login page. All of this commits in one transaction. - Throttling. Each address may request three links in fifteen minutes, from any client,
counted whether or not an account uses it;
PasswordReset::throttlechanges the limit. Past it the page answers 429. A limit per client belongs at the proxy. - The resetter’s
UserisU. Mounting refuses a reset onAuth::disabled(), a reset withoutAuth::mail, a base URL that is not an absolutehttporhttpsURL or that carries a query or fragment, and a router without aMailConfig.
Sourcepub fn email_verification(self, verification: EmailVerification<U>) -> Self
pub fn email_verification(self, verification: EmailVerification<U>) -> Self
Makes each signed-in user prove they read the address PanelUser::email answers before
they reach anything but {prefix}/verify-email, which mails them a link through
Auth::mail. A sign-up mails the link at once, after the response.
- Until the user verifies, a page redirects to
{prefix}/verify-emailand any other request answers 403. The page offers to mail a link, and to sign out. A user whosePanelUser::emailisNonehas no address to prove and is not held. - Accounts that predate verification are unverified: turning it on sends each of them,
seeded admins included, to
{prefix}/verify-emailat their next request. Backfillemail_verified_at(fromcreated_at, say) for the addresses you already trust. - A link works once, for one day, on the panel that mailed it, and only while the account
may enter the panel and still stores the address it was mailed to. It verifies on
GETin any browser, signed in or not, then lands on the panel or on the login page. A mail scanner that opens the link verifies the address too: it fetched the link from the mailbox the link proves. - A new email waits for its own link. A profile editor calls
auth::request_email_changefromsavein place of writing the address: on a panel with email verification it records the change and answersEmailChange::Pending, and once the save commits the panel mails the new address a link that stores it and the old address a link that keeps it. Elsewhere it answersEmailChange::Immediateand the editor stores the address itself.PasswordAuthdoes this. The link proves only that whoever asked reads the new mailbox, so ask for the current password before calling it, asPasswordAuthdoes: a stolen session could name its thief’s mailbox. - Either link opens a page whose button does the work, so a mail scanner that fetches it changes nothing.
- The new address’s link stores it, verified, once, for one day. It is a credential change: the account’s sessions and sign-in links end on every panel signing in its user type, and the browser that confirmed keeps a new session when it was signed in as the account.
- The old address’s link works for a week, before or after the change goes through: it keeps the old address, restoring it if the change went through, and ends every session, sign-in link and pending change the account holds, the cancelling browser’s included: whoever asked for the change may hold one.
- Throttling. Each account may ask for three links in fifteen minutes;
EmailVerification::throttlechanges the limit. Past it the page answers 429. - The verifier’s
UserisU. Mounting refuses a verification onAuth::disabled(), and one withoutAuth::mail.
Sourcepub fn tenant_registration(self, registration: TenantRegistration<U>) -> Self
pub fn tenant_registration(self, registration: TenantRegistration<U>) -> Self
Lets the signed-in user create a tenant to act for at {prefix}/register-tenant, through
registration’s TenantRegistrar; the tenant menu links to
it from one membership on. TenantRegistration::required sends a user who acts for no
tenant there first.
The registrar’s Input is the page’s form; validate and register run in one
transaction, and register writes the tenant and the user’s membership of it, answering
that Membership. The session then acts for the new tenant, and the
next request’s find_by_id must list it.
The registrar’s User is U. Mounting refuses a
registration on Auth::disabled, a form field named csrf_token, a file field and a
relationship choice.
Sourcepub fn tenant_profile(self, editor: impl TenantEditor<User = U>) -> Self
pub fn tenant_profile(self, editor: impl TenantEditor<User = U>) -> Self
Lets the signed-in user edit the tenant they act for at {prefix}/tenant/profile, through
editor; the tenant menu links to it for a user TenantEditor::may_edit admits, and the
page answers 403 to anyone else. fill loads the form from the tenant, and validate and
save run in one transaction. The form names the tenant it was filled from, so one posted
after another tab switched tenants saves nothing.
The editor’s User is U. Mounting refuses a settings page on
Auth::disabled, a form field named csrf_token or editing_tenant, a file field and a
relationship choice.
Sourcepub fn two_factor(self, two_factor: TwoFactor) -> Self
pub fn two_factor(self, two_factor: TwoFactor) -> Self
Lets each signed-in user add an authenticator app at {prefix}/two-factor; the user menu
links to it. An account with one signs in with a code from it, or a recovery code, at
{prefix}/two-factor-challenge after its password.
- Setting up happens at
{prefix}/two-factor, which the user menu links to. It asks for the current password first, throughAuthenticator::confirm_password, so a stolen session cannot add a factor its thief holds. That method verifies with the user’sPanelUser::emailas the login; override it when users sign in with something else. The page then shows a QR code and the key, and turns two-factor sign-in on once a code from the app matches. It then shows eight recovery codes, once: the panel stores only their hashes, keyed like the secrets. - Each panel enrols apart. A factor signs in to the panel that set it up only, even when other panels load the same user type: enrol on each.
- Signing in, a correct password leads to
{prefix}/two-factor-challengeinstead of a session. The challenge lasts five minutes in its own cookie and takes a code from the app or a recovery code. Each code works once: an app code does not sign in twice, and a recovery code is used up. - Wrong codes. Every fifth wrong code in a row ends the challenge, so the next try takes the
password again. Past the fifth, each code of the app or the mail waits for the one before
it: 30 seconds, doubling up to an hour, across challenges, until a right code; a recovery
code never waits. Each fifth mails the account, through
Auth::mail, that its password signed in and codes failed. Someone holding the password can so keep the app’s codes waiting and make the owner spend recovery codes; the mail tells the owner, and a new password ends it. - Turning it on or off is a credential change: the account’s other sessions and links end. Turning it off, and showing new recovery codes, take a code.
- Codes by email.
TwoFactor::new().email_codes()lets a user with an address choose codes mailed to it in place of an app, throughAuth::mail. Each sign-in mails a six-digit code that works once, for ten minutes; the challenge page mails a new one on request, in place of the last. Each account may have three mailed in fifteen minutes;TwoFactor::mail_throttlechanges the limit. An account holds one factor at a time: it turns one off before setting up the other. A panel that dropsemail_codes()mails no more codes: an account set up for them signs in with a recovery code. A mail server that fails leaves the challenge up, with its button to send another code. - Throttling. Each account may try five codes or passwords a minute, at sign-in and on the
settings page together;
TwoFactor::throttlechanges the limit. Past it the page answers 429. The count lives in each process: behind several, the limit holds per process. Someone who knows the password can use up the owner’s attempts, and each new sign-in replaces the last challenge. - The cookie
Keyseals the secrets. Rotating it makes every app code fail until its owner signs in with a recovery code and sets the app up again; a leaked key opens every secret. - Requiring it.
TwoFactor::new().required()sends a signed-in user without a second factor to{prefix}/two-factorbefore anything else, as an unverified address does: a page redirects there and any other request answers 403. - Mounting refuses two-factor sign-in on
Auth::disabled(), a router without a cookieKey, and codes by email withoutAuth::mail.
Sourcepub fn mail(self, mail: AuthMail) -> Self
pub fn mail(self, mail: AuthMail) -> Self
Sends the panel’s auth mail, such as reset links, from mail’s sender, with links that
start with its base URL. Mounting refuses a base URL that is not absolute, and a router
without a MailConfig.
Sourcepub fn is_disabled(&self) -> bool
pub fn is_disabled(&self) -> bool
Whether the explicit opt-out is set.