pub struct Auth { /* private fields */ }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
impl Auth
Sourcepub fn guard(&self, user: &User, session: &Session) -> SessionGuard
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
impl Auth
Sourcepub async fn dispatch(
&self,
caller: &Caller<'_>,
request: AuthRequest,
) -> Result<Dispatched>
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
impl Auth
Sourcepub async fn cloudflare(&self) -> Result<Option<CloudflareConfig>>
Available on crate feature cloudflare only.
pub async fn cloudflare(&self) -> Result<Option<CloudflareConfig>>
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.
Sourcepub async fn set_cloudflare(
&self,
actor: &User,
session: &Session,
config: CloudflareConfig,
) -> Result<CloudflareConfig>
Available on crate feature cloudflare only.
pub async fn set_cloudflare( &self, actor: &User, session: &Session, config: CloudflareConfig, ) -> Result<CloudflareConfig>
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.
Sourcepub async fn cloudflare_available(&self, headers: &HeaderMap) -> Result<bool>
Available on crate feature cloudflare only.
pub async fn cloudflare_available(&self, headers: &HeaderMap) -> Result<bool>
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.
Sourcepub async fn sign_in_cloudflare(
&self,
headers: &HeaderMap,
) -> Result<(User, Session)>
Available on crate feature cloudflare only.
pub async fn sign_in_cloudflare( &self, headers: &HeaderMap, ) -> Result<(User, Session)>
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
impl Auth
Sourcepub async fn test_user(&self, new: &NewUser, password: &str) -> Result<User>
Available on crate feature test-util only.
pub async fn test_user(&self, new: &NewUser, password: &str) -> Result<User>
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.
Sourcepub async fn test_session(&self, user: &User) -> Result<Session>
Available on crate feature test-util only.
pub async fn test_session(&self, user: &User) -> Result<Session>
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
impl Auth
Sourcepub fn builder<S>(store: S, session_key: [u8; 32]) -> AuthBuilderwhere
S: Store,
pub fn builder<S>(store: S, session_key: [u8; 32]) -> AuthBuilderwhere
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.
Sourcepub async fn builder_from_store<S>(store: S) -> Result<AuthBuilder, StoreError>where
S: Store,
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();Sourcepub fn uses_logins(&self) -> bool
pub fn uses_logins(&self) -> bool
Whether users have login names; see AuthBuilder::logins.
Sourcepub fn offers_google(&self) -> bool
pub fn offers_google(&self) -> bool
Whether Google sign-in is configured; see AuthBuilder::google
(feature google). This is AuthConfig::google.
Sourcepub fn allows_impersonation(&self) -> bool
pub fn allows_impersonation(&self) -> bool
Whether admins may impersonate users; see
AuthBuilder::impersonation.
Sourcepub fn client_address(&self, peer: IpAddr, headers: &HeaderMap) -> IpAddr
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.
Sourcepub fn session_id(&self, headers: &HeaderMap) -> Option<String>
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.
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.
Sourcepub async fn session(&self, session_id: &str) -> Result<Option<(User, Session)>>
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.
Sourcepub async fn prune(&self) -> Result<()>
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.
Sourcepub async fn session_info(
&self,
user: &User,
session: &Session,
) -> Result<UserInfo>
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.
Sourcepub async fn sign_in_password(
&self,
address: IpAddr,
request: &LoginRequest,
) -> Result<(User, Session)>
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.
Sourcepub async fn find_user(&self, name: &str) -> Result<Option<User>>
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.
Sourcepub async fn register_info(&self, token: &str) -> Result<RegisterInfo>
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.
Sourcepub async fn register(
&self,
token: &str,
request: &RegisterRequest,
) -> Result<(User, Session)>
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.
Sourcepub async fn change_password(
&self,
user: &User,
session: &Session,
change: &PasswordChange,
) -> Result<User>
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.
Sourcepub async fn users(&self, actor: &User) -> Result<Vec<UserEntry>>
pub async fn users(&self, actor: &User) -> Result<Vec<UserEntry>>
Every user, with their live link’s expiry. Admins only.
Sourcepub async fn create_user(
&self,
actor: &User,
session: &Session,
new: &NewUser,
) -> Result<(UserEntry, Link)>
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.
Sourcepub async fn issue_link(
&self,
actor: &User,
session: &Session,
id: UserId,
) -> Result<Link>
pub async fn issue_link( &self, actor: &User, session: &Session, id: UserId, ) -> Result<Link>
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.
Sourcepub async fn revoke_link(
&self,
actor: &User,
session: &Session,
id: UserId,
) -> Result<()>
pub async fn revoke_link( &self, actor: &User, session: &Session, id: UserId, ) -> Result<()>
Uses up the user’s links, so none of them works. Admins only.
Sourcepub async fn sign_out_everywhere(
&self,
actor: &User,
session: &Session,
id: UserId,
) -> Result<()>
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.
Sourcepub async fn revoke_access(
&self,
actor: &User,
session: &Session,
id: UserId,
) -> Result<User>
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.
Sourcepub async fn set_role(
&self,
actor: &User,
session: &Session,
id: UserId,
role: Role,
) -> Result<User>
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.
Sourcepub async fn update_user(
&self,
actor: &User,
session: &Session,
id: UserId,
change: &UserChange,
) -> Result<User>
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.
Sourcepub async fn delete_user(
&self,
actor: &User,
session: &Session,
id: UserId,
) -> Result<()>
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.
Sourcepub async fn impersonate(
&self,
actor: &User,
session: &Session,
id: UserId,
) -> Result<(User, Session)>
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.
Sourcepub async fn stop_impersonating(
&self,
session: &Session,
) -> Result<(User, Session)>
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.
Sourcepub async fn bootstrap_admin(&self, admin: &BootstrapAdmin) -> Result<Bootstrap>
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.
Sourcepub async fn recovery_link(&self, name: &str) -> Result<Link>
pub async fn recovery_link(&self, name: &str) -> Result<Link>
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.
Sourcepub async fn import_user(&self, new: &NewUser) -> Result<User>
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§
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§
impl<Token, Builder, How> AllPropsFor<Builder, How> for Tokenwhere
Builder: Buildable<Token>,
<Builder as Buildable<Token>>::WrappedToken: HasAllProps<<Builder as Buildable<Token>>::Output, How>,
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> HasAllProps<(), T> for T
impl<A, B, T> HttpServerConnExec<A, B> for Twhere
B: Body,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoPropValue<Option<T>> for T
impl<T> IntoPropValue<Option<T>> for T
Source§fn into_prop_value(self) -> Option<T>
fn into_prop_value(self) -> Option<T>
self to a value of a Properties struct.Source§impl<T> IntoPropValue<T> for T
impl<T> IntoPropValue<T> for T
Source§fn into_prop_value(self) -> T
fn into_prop_value(self) -> T
self to a value of a Properties struct.