Skip to main content

Auth

Struct Auth 

Source
pub struct Auth { /* private fields */ }
Available on crate feature server only.
Expand description

The authentication service: sessions, cookies, sign-in, registration links and user management over a Store, enforcing the rules of docs/design.md section 3.

Cloning it is cheap and shares the same service.

Operations that act on behalf of a signed-in user take that user as actor, as loaded for the current request. Admin operations refuse a non-admin actor with AuthError::Forbidden. Operations that change something also take the session the actor signed in with, so that the AuditEvents they send name an admin impersonating the actor (Actor::User::impersonator).

Implementations§

Source§

impl Auth

Source

pub fn guard(&self, user: &User, session: &Session) -> SessionGuard

Watches session, signed in as user (as loaded for the request, such as the SignedIn extractor’s fields), for a WebSocket’s select! loop:

let guard = auth.guard(&user, &session);

loop {
    tokio::select! {
        _ = guard.ended() => break,
        info = guard.changed() => {
            // The user's role, email, login or credential changed.
        }
        // ... the socket's own messages
    }
}

The guard follows AuthEvents, sleeps until the session expires, and re-checks the session in the store on broadcast lag and every 5 minutes, so a session deleted by another process still ends the socket. It runs a task on the current Tokio runtime until it is dropped or the session ends.

Source§

impl Auth

Source

pub async fn dispatch( &self, caller: &Caller<'_>, request: AuthRequest, ) -> Result<Dispatched>

Runs one AuthRequest for caller.

Every transport’s requests come through here: the axum08 routes and the musli-web Connection decode their own request into an AuthRequest and answer with the Dispatched response, so an operation behaves the same over each. The checks happen here: a request that needs a signed-in user is refused with AuthError::Unauthorized without a live session, an admin request with AuthError::Forbidden unless the user is an active admin now, and the operations enforce the rest of their rules themselves.

The requests whose cookie is set answer with a SessionCookie in Dispatched::cookie, which the caller must apply for the user to be signed in (or out) as answered.

Without the cloudflare feature, the Cloudflare requests are refused with AuthError::NotFound, after the same checks as with it. AuthRequest::Login needs the caller’s address and is refused with AuthError::Forbidden, logged as an error, without one.

Errors with Error::Auth for a refusal, and otherwise when the store fails or hashing a password does.

Source§

impl Auth

Source

pub async fn cloudflare(&self) -> Result<Option<CloudflareConfig>>

Available on crate feature cloudflare only.

The Cloudflare Access settings: those kept in the store, or those given to AuthBuilder::cloudflare while the store has none, or None when neither has any. They are what an admin’s settings page shows, enabled or not.

The store is read once, the first time the settings are needed, which is also when they are logged. To have them logged at startup, call this there.

Errors only when the store fails.

Source

pub async fn set_cloudflare( &self, actor: &User, session: &Session, config: CloudflareConfig, ) -> Result<CloudflareConfig>

Available on crate feature cloudflare only.

Stores the Cloudflare Access settings, under the store’s cloudflare setting, and applies them, such as when an admin saves them on the settings page. Returns them as stored, with the team domain and the audience trimmed.

Settings that are not enabled turn Access sign-in off. Settings that differ from the current ones are logged, with a warning when they are unsafe or let no one sign in; the same settings again change nothing.

Sends AuditEvent::CloudflareChanged when the settings differ from the current ones, naming an admin impersonating actor through session.

Admins only: AuthError::Forbidden unless actor is an active admin. An app moving settings it kept itself onto watchword passes them to AuthBuilder::cloudflare, where they apply until an admin saves settings here.

Source

pub async fn cloudflare_available(&self, headers: &HeaderMap) -> Result<bool>

Available on crate feature cloudflare only.

Whether the request carries Cloudflare Access credentials that sign in an existing, not revoked user, as sign_in_cloudflare would. This is AuthConfig::cloudflare.

When the request carries Access credentials that do not, the reason is logged as a warning, at most once a minute.

Source

pub async fn sign_in_cloudflare( &self, headers: &HeaderMap, ) -> Result<(User, Session)>

Available on crate feature cloudflare only.

Signs in the user whose email the request’s Cloudflare Access credentials vouch for, whatever their Method.

A pending user becomes active, because Access has vouched for their email. Anything else that is not an active user, including a revoked user, an unknown email and Access being disabled, is AuthError::Unauthorized, with the reason logged as for cloudflare_available.

Source§

impl Auth

Source

pub async fn test_user(&self, new: &NewUser, password: &str) -> Result<User>

Available on crate feature test-util only.

Creates an active user who signs in with password, for a test.

new is checked and normalized as create_user does, and the user is created as import_user does, with no actor, then given the password, which makes them active. It sends the same AuthEvents and AuditEvents (from Actor::System) as those steps. The password is hashed at bcrypt’s lowest cost, so it is quick to hash and to check with sign_in_password, and it is not checked against the password rules, so a test may use a short one.

Never reach this from a request: like import_user, it has no actor.

Source

pub async fn test_session(&self, user: &User) -> Result<Session>

Available on crate feature test-util only.

Creates and stores a session for user, not impersonated, which lasts the usual SESSION_LIFETIME, for a test to act as user without signing in.

Use it as the actor’s session of an operation such as create_user, or send it with a request through cookie_header. It is created whatever the user’s access: session and the extractors still refuse it when the user may not hold one, as for any session.

Source§

impl Auth

Source

pub fn builder<S>(store: S, session_key: [u8; 32]) -> AuthBuilder
where S: Store,

Starts building the service over store, signing cookies with session_key (see load_or_create_key). To use the key the store keeps, use builder_from_store.

Source

pub async fn builder_from_store<S>(store: S) -> Result<AuthBuilder, StoreError>
where S: Store,

Starts building the service over store, signing cookies with the key the store keeps, which load_or_create_key loads or creates.

This is how an app’s server and its offline tools (an import, a backup restore, a recovery command) open the same store with the same key:

use watchword::{Auth, MemoryStore};

let auth = Auth::builder_from_store(MemoryStore::new()).await?.build();
Source

pub fn uses_logins(&self) -> bool

Whether users have login names; see AuthBuilder::logins.

Source

pub fn offers_google(&self) -> bool

Whether Google sign-in is configured; see AuthBuilder::google (feature google). This is AuthConfig::google.

Source

pub fn allows_impersonation(&self) -> bool

Whether admins may impersonate users; see AuthBuilder::impersonation.

Source

pub fn events(&self) -> Receiver<AuthEvent>

Subscribes to AuthEvents.

Source

pub fn client_address(&self, peer: IpAddr, headers: &HeaderMap) -> IpAddr

The address a request from peer with headers is throttled by: the forwarded client address when peer is a trusted proxy, otherwise peer.

Source

pub fn session_id(&self, headers: &HeaderMap) -> Option<String>

The session id of the request’s correctly signed session cookie, if it has one. The session may still have ended; see session.

The Set-Cookie value that signs in the session session_id, for a response to a request with headers.

Source

pub fn cookie_header(&self, session_id: &str) -> HeaderValue

The Cookie request header that carries the session session_id: the name=value pair of set_cookie without its attributes, as a browser sends it back. For a test or a client that makes requests as a session, such as one from Auth::test_session (feature test-util).

The Set-Cookie value that removes the session cookie.

Source

pub async fn session(&self, session_id: &str) -> Result<Option<(User, Session)>>

The live session session_id and its user: None if the session does not exist, has expired, or its user was deleted or revoked, and for an impersonated session also when its impersonator is no longer an active admin (deleted, revoked or demoted), which deletes the session. Pending users hold a session only through impersonation.

Source

pub async fn prune(&self) -> Result<()>

Deletes the sessions that have expired, and the registration and reset links that expired more than TOKEN_LIFETIME ago, used or not. Until then an expired link still answers AuthError::Gone rather than AuthError::NotFound.

Creating a session may also delete expired sessions. Call it on a timer to keep the store small, or have AuthBuilder::prune_every do so.

Source

pub async fn session_info( &self, user: &User, session: &Session, ) -> Result<UserInfo>

What the frontend sees of user, signed in with session: their UserInfo with impersonated_by set when an admin is acting as them.

Source

pub async fn sign_in_password( &self, address: IpAddr, request: &LoginRequest, ) -> Result<(User, Session)>

Signs in with a login name or email and a password, from the client address (see client_address).

Only active users with a password get a session. Every failure is AuthError::Unauthorized and takes as long as checking a password, and failures are throttled per address and per login with AuthError::TooManyRequests.

Source

pub async fn find_user(&self, name: &str) -> Result<Option<User>>

The user named name, as password sign-in finds them: by login name first (trimmed, when logins are used), then by normalized email. None when neither names a user, or name is blank.

It checks no actor, so an app gates it as it needs.

Source

pub async fn sign_out(&self, session_id: &str) -> Result<()>

Ends the session session_id.

Source

pub async fn register_info(&self, token: &str) -> Result<RegisterInfo>

What the registration or reset link token is for.

AuthError::NotFound for an unknown link, AuthError::Gone for one that was used or has expired.

Source

pub async fn register( &self, token: &str, request: &RegisterRequest, ) -> Result<(User, Session)>

Registers, or resets the credential, with the link token and a password. The user becomes active with a password, all of their sessions end, and the returned one is created.

Errors as register_info does, and with AuthError::Invalid for an unacceptable password.

Source

pub async fn change_password( &self, user: &User, session: &Session, change: &PasswordChange, ) -> Result<User>

Changes the signed-in user’s password, keeping session and ending their other sessions. A Google user sets a password this way, which switches them back to a password.

The current password is required when one is set: AuthError::Invalid when it is wrong or the new one is not acceptable.

Source

pub async fn users(&self, actor: &User) -> Result<Vec<UserEntry>>

Every user, with their live link’s expiry. Admins only.

Source

pub async fn create_user( &self, actor: &User, session: &Session, new: &NewUser, ) -> Result<(UserEntry, Link)>

Creates a pending user and issues their registration link. Admins only.

AuthError::Invalid without a login or email (an email when logins are not used), AuthError::Conflict when either is taken.

Issues a new link to the user id, using up their earlier ones: a reset link for an active user, a registration link otherwise. A revoked user regains access by registering with it. Admins only.

Uses up the user’s links, so none of them works. Admins only.

Source

pub async fn sign_out_everywhere( &self, actor: &User, session: &Session, id: UserId, ) -> Result<()>

Ends all of the user’s sessions; they can sign in again. Admins only, and not on their own account.

Source

pub async fn revoke_access( &self, actor: &User, session: &Session, id: UserId, ) -> Result<User>

Revokes the user’s access: they cannot sign in by any path, their password is cleared, their links are used up and their sessions end. A new link restores access. Admins only, and not on their own account.

Source

pub async fn set_role( &self, actor: &User, session: &Session, id: UserId, role: Role, ) -> Result<User>

Sets the user’s role. Admins only, and not their own role.

Source

pub async fn update_user( &self, actor: &User, session: &Session, id: UserId, change: &UserChange, ) -> Result<User>

Changes the user’s login, email or role.

Admins may change any user, except their own role. Anyone else may change only their own login and email, and a Google user’s own email is the one Google verified, so it cannot be changed this way. The user must keep a login or an email (an email when logins are not used). AuthError::Conflict when the login or email is taken.

Source

pub async fn delete_user( &self, actor: &User, session: &Session, id: UserId, ) -> Result<()>

Deletes the user, with their sessions and links. Admins only, and not their own account.

Source

pub async fn impersonate( &self, actor: &User, session: &Session, id: UserId, ) -> Result<(User, Session)>

Creates a session as the user id, for an admin to see what they see. The session records the admin as its impersonator, which session_info shows and stop_impersonating returns to.

Pending users can be impersonated, revoked ones cannot. Admins only, when enabled, not themselves, and not from session if it is itself impersonated (an admin acting as another admin), so that stopping always leads back to the admin who started.

Source

pub async fn stop_impersonating( &self, session: &Session, ) -> Result<(User, Session)>

Ends the impersonated session and creates a session for the admin who started it, returning the admin and the new session.

AuthError::Forbidden, with nothing changed, when session is not impersonated, or its admin is no longer an active admin. The latter only happens between loading session and this call, since session already ends such a session.

Source

pub async fn bootstrap_admin(&self, admin: &BootstrapAdmin) -> Result<Bootstrap>

Brings up the first admin, so the option can stay set across restarts:

  • With no users, creates the admin, pending, and issues their registration link: Bootstrap::Created.
  • When the named admin is still pending, no active admin exists and their link has expired (or was used up), issues a new one: Bootstrap::Reissued.
  • When that admin’s link is still live, changes nothing: Bootstrap::Pending.
  • Otherwise, changes nothing: Bootstrap::Skipped.

It logs the outcome at info, with the link as a full URL under BootstrapAdmin::public_url if set, otherwise as the path. An app whose log the operator may not see prints the link itself, with Link::url.

Issues a new link to the user named name, for an app’s own command line to recover an account with no admin to act: a reset link for an active user, a registration link otherwise, which restores a revoked user. It uses up the user’s earlier links, as issue_link does, but acts as the system, so it needs no actor; never expose it to a request.

name is a login, or else an email, as for find_user. AuthError::NotFound when neither names a user.

Source

pub async fn import_user(&self, new: &NewUser) -> Result<User>

Creates a pending user without issuing them a link, for an app’s own import or backup-restore tool. It validates and normalizes new as create_user does and sends AuthEvent::UserCreated and AuditEvent::UserCreated, but acts as the system, so it needs no actor; never expose it to a request.

The user signs in once they have a link, from issue_link or recovery_link.

AuthError::Invalid without a login or email (an email when logins are not used), AuthError::Conflict when either is taken.

Trait Implementations§

Source§

impl Clone for Auth

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more

Auto Trait Implementations§

§

impl !RefUnwindSafe for Auth

§

impl !UnwindSafe for Auth

§

impl Freeze for Auth

§

impl Send for Auth

§

impl Sync for Auth

§

impl Unpin for Auth

§

impl UnsafeUnpin for Auth

Blanket Implementations§

Source§

impl<Token, Builder, How> AllPropsFor<Builder, How> for Token
where Builder: Buildable<Token>, <Builder as Buildable<Token>>::WrappedToken: HasAllProps<<Builder as Buildable<Token>>::Output, How>,

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> FromRef<T> for T
where T: Clone,

Source§

fn from_ref(input: &T) -> T

Converts to this type from a reference to the input type.
Source§

impl<T> HasAllProps<(), T> for T

Source§

impl<A, B, T> HttpServerConnExec<A, B> for T
where B: Body,

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoPropValue<Option<T>> for T

Source§

fn into_prop_value(self) -> Option<T>

Convert self to a value of a Properties struct.
Source§

impl<T> IntoPropValue<T> for T

Source§

fn into_prop_value(self) -> T

Convert self to a value of a Properties struct.
Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<S, T> Upcast<T> for S
where T: UpcastFrom<S> + ?Sized, S: ?Sized,

Source§

fn upcast(&self) -> &T
where Self: ErasableGeneric, T: Sized + ErasableGeneric<Repr = Self::Repr>,

Perform a zero-cost type-safe upcast to a wider ref type within the Wasm bindgen generics type system. Read more
Source§

fn upcast_into(self) -> T
where Self: Sized + ErasableGeneric, T: Sized + ErasableGeneric<Repr = Self::Repr>,

Perform a zero-cost type-safe upcast to a wider type within the Wasm bindgen generics type system. Read more
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more