pub struct Db { /* private fields */ }Expand description
Handle to the allowthem database.
Wraps a SqlitePool and guarantees that migrations have been applied.
All query code receives a &Db and calls db.pool() to access the pool.
Implementations§
Source§impl Db
impl Db
Sourcepub async fn validate_access_token(
&self,
token: &str,
expected_issuer: &str,
) -> Result<AccessTokenClaims, AuthError>
pub async fn validate_access_token( &self, token: &str, expected_issuer: &str, ) -> Result<AccessTokenClaims, AuthError>
Validate an RS256-signed access token JWT.
Steps:
- Decode the JWT header to extract
kid. - Look up the signing key by
kidin the database. - Verify the RS256 signature using the public key PEM.
- Check
expagainst the current time. - Verify
issmatchesexpected_issuer. - Parse
subasUserIdand returnAccessTokenClaims.
Source§impl Db
impl Db
Sourcepub async fn create_api_token(
&self,
user_id: UserId,
name: &str,
expires_at: Option<DateTime<Utc>>,
metadata: Option<&str>,
) -> Result<(String, ApiTokenInfo), AuthError>
pub async fn create_api_token( &self, user_id: UserId, name: &str, expires_at: Option<DateTime<Utc>>, metadata: Option<&str>, ) -> Result<(String, ApiTokenInfo), AuthError>
Generate and store a new API token for the user.
Returns the raw token string (shown once, never stored) and
ApiTokenInfo metadata. The caller must present the raw token to the
user — it cannot be retrieved again.
Sourcepub async fn validate_api_token(
&self,
raw_token: &str,
) -> Result<Option<(UserId, ApiTokenInfo)>, AuthError>
pub async fn validate_api_token( &self, raw_token: &str, ) -> Result<Option<(UserId, ApiTokenInfo)>, AuthError>
Validate a raw bearer token.
Hashes the token and queries by hash. Tokens with a past expires_at
are excluded. Returns Some((UserId, ApiTokenInfo)) if valid, None otherwise.
Sourcepub async fn list_api_tokens(
&self,
user_id: UserId,
) -> Result<Vec<ApiTokenInfo>, AuthError>
pub async fn list_api_tokens( &self, user_id: UserId, ) -> Result<Vec<ApiTokenInfo>, AuthError>
List all API tokens for a user (metadata only, no hashes).
Sourcepub async fn delete_api_token(&self, id: ApiTokenId) -> Result<bool, AuthError>
pub async fn delete_api_token(&self, id: ApiTokenId) -> Result<bool, AuthError>
Delete a single API token by ID.
Returns true if a token was found and deleted.
Source§impl Db
impl Db
Sourcepub async fn create_application(
&self,
name: String,
redirect_uris: Vec<String>,
is_trusted: bool,
created_by: Option<UserId>,
logo_url: Option<String>,
primary_color: Option<String>,
) -> Result<(Application, ClientSecret), AuthError>
pub async fn create_application( &self, name: String, redirect_uris: Vec<String>, is_trusted: bool, created_by: Option<UserId>, logo_url: Option<String>, primary_color: Option<String>, ) -> Result<(Application, ClientSecret), AuthError>
Register a new OIDC application.
Generates a client_id and client_secret, hashes the secret, and inserts
the row. Returns the persisted Application and the raw ClientSecret.
The raw secret is shown once and is not recoverable — the caller must present
it to the admin immediately.
Validates redirect_uris before inserting. Returns AuthError::InvalidRedirectUri
if any URI fails validation.
Sourcepub async fn get_application(
&self,
id: ApplicationId,
) -> Result<Application, AuthError>
pub async fn get_application( &self, id: ApplicationId, ) -> Result<Application, AuthError>
Get an application by internal ID.
Sourcepub async fn get_application_by_client_id(
&self,
client_id: &ClientId,
) -> Result<Application, AuthError>
pub async fn get_application_by_client_id( &self, client_id: &ClientId, ) -> Result<Application, AuthError>
Get an application by its public client_id.
Used by OAuth endpoints that receive client_id in request parameters.
Sourcepub async fn get_branding_by_client_id(
&self,
client_id: &ClientId,
) -> Result<Option<BrandingConfig>, AuthError>
pub async fn get_branding_by_client_id( &self, client_id: &ClientId, ) -> Result<Option<BrandingConfig>, AuthError>
Get branding configuration for an application by client_id.
Returns None if no application with the given client_id exists
or if the application is inactive. Branded pages fall back to
default allowthem styling when this returns None.
Sourcepub async fn list_applications(&self) -> Result<Vec<Application>, AuthError>
pub async fn list_applications(&self) -> Result<Vec<Application>, AuthError>
List all applications ordered by created_at ASC.
Sourcepub async fn update_application(
&self,
id: ApplicationId,
params: UpdateApplication,
) -> Result<(), AuthError>
pub async fn update_application( &self, id: ApplicationId, params: UpdateApplication, ) -> Result<(), AuthError>
Update an application’s mutable fields.
Validates redirect_uris, serializes them to JSON, and writes all six
mutable fields atomically. Caller is responsible for fetching the current
application and populating unchanged fields.
Returns AuthError::NotFound if no application with id exists.
Returns AuthError::InvalidRedirectUri if any URI fails validation.
Sourcepub async fn regenerate_client_secret(
&self,
id: ApplicationId,
) -> Result<(Application, ClientSecret), AuthError>
pub async fn regenerate_client_secret( &self, id: ApplicationId, ) -> Result<(Application, ClientSecret), AuthError>
Generate a new client secret, invalidating the previous one.
Returns the updated Application and the raw ClientSecret.
The new secret is the only opportunity to retrieve it — the old secret
is irrecoverably invalidated on success.
Returns AuthError::NotFound if no application with id exists.
Sourcepub async fn delete_application(
&self,
id: ApplicationId,
) -> Result<(), AuthError>
pub async fn delete_application( &self, id: ApplicationId, ) -> Result<(), AuthError>
Permanently delete an application and all associated grants.
Cascade deletes: authorization_codes, refresh_tokens, consents.
Returns AuthError::NotFound if no application with id exists.
Source§impl Db
impl Db
Sourcepub async fn log_audit(
&self,
event_type: AuditEvent,
user_id: Option<&UserId>,
target_id: Option<&str>,
ip_address: Option<&str>,
user_agent: Option<&str>,
detail: Option<&str>,
) -> Result<(), AuthError>
pub async fn log_audit( &self, event_type: AuditEvent, user_id: Option<&UserId>, target_id: Option<&str>, ip_address: Option<&str>, user_agent: Option<&str>, detail: Option<&str>, ) -> Result<(), AuthError>
Record an audit event.
user_id may be None for events where no authenticated user is
involved (e.g. a failed login attempt against an unknown email).
Sourcepub async fn get_audit_log(
&self,
user_id: Option<&UserId>,
limit: u32,
offset: u32,
) -> Result<Vec<AuditEntry>, AuthError>
pub async fn get_audit_log( &self, user_id: Option<&UserId>, limit: u32, offset: u32, ) -> Result<Vec<AuditEntry>, AuthError>
Retrieve audit log entries, optionally filtered by user.
Results are ordered by created_at descending (newest first).
Sourcepub async fn get_audit_log_by_event(
&self,
event_type: AuditEvent,
limit: u32,
offset: u32,
) -> Result<Vec<AuditEntry>, AuthError>
pub async fn get_audit_log_by_event( &self, event_type: AuditEvent, limit: u32, offset: u32, ) -> Result<Vec<AuditEntry>, AuthError>
Retrieve audit log entries filtered by event type.
Results are ordered by created_at descending (newest first).
Sourcepub async fn last_login_at(
&self,
user_id: UserId,
) -> Result<Option<DateTime<Utc>>, AuthError>
pub async fn last_login_at( &self, user_id: UserId, ) -> Result<Option<DateTime<Utc>>, AuthError>
Get the most recent login timestamp for a user, if any.
Returns None if the user has never logged in (no audit entry
with event_type = ‘login’ for this user_id).
Sourcepub async fn search_audit_log(
&self,
params: SearchAuditParams<'_>,
) -> Result<SearchAuditResult, AuthError>
pub async fn search_audit_log( &self, params: SearchAuditParams<'_>, ) -> Result<SearchAuditResult, AuthError>
Search and filter audit log entries with pagination.
Builds a dynamic query with optional filters for user, event type,
outcome, and date range. LEFT JOINs allowthem_users for email
resolution. Follows the same dynamic-SQL pattern as search_users.
Source§impl Db
impl Db
Sourcepub async fn has_sufficient_consent(
&self,
user_id: UserId,
application_id: ApplicationId,
requested_scopes: &[String],
) -> Result<bool, AuthError>
pub async fn has_sufficient_consent( &self, user_id: UserId, application_id: ApplicationId, requested_scopes: &[String], ) -> Result<bool, AuthError>
Check whether the user has an existing consent that covers all requested scopes.
Sourcepub async fn upsert_consent(
&self,
user_id: UserId,
application_id: ApplicationId,
scopes: &[String],
) -> Result<(), AuthError>
pub async fn upsert_consent( &self, user_id: UserId, application_id: ApplicationId, scopes: &[String], ) -> Result<(), AuthError>
Record or update user consent for an application.
Stored scopes become the union of existing and new scopes (consent is additive).
Sourcepub async fn get_consent(
&self,
user_id: UserId,
application_id: ApplicationId,
) -> Result<Option<Consent>, AuthError>
pub async fn get_consent( &self, user_id: UserId, application_id: ApplicationId, ) -> Result<Option<Consent>, AuthError>
Get the consent record for a user and application, if any.
Create an authorization code record. Expires after 10 minutes.
Look up an authorization code by its hash.
Mark an authorization code as used.
Source§impl Db
impl Db
Sourcepub async fn new(pool: SqlitePool) -> Result<Self, AuthError>
pub async fn new(pool: SqlitePool) -> Result<Self, AuthError>
Create a Db from an integrator-provided pool and run migrations.
This is the embedded-mode constructor. The caller is responsible for
configuring PRAGMA foreign_keys = ON on their pool’s
SqliteConnectOptions — this constructor cannot set per-connection
pragmas on a pool it did not create.
Migrations are idempotent: safe to call on a pool that has already
been migrated. SQLx tracks applied migrations in _sqlx_migrations
and CREATE TABLE IF NOT EXISTS in the SQL is a no-op on existing tables.
ignore_missing is set so that migrations from the integrating application
already recorded in _sqlx_migrations do not cause an error — those are
the integrator’s own migrations, not allowthem’s.
Sourcepub async fn connect(url: &str) -> Result<Self, AuthError>
pub async fn connect(url: &str) -> Result<Self, AuthError>
Create a pool from a URL, apply pragmas, run migrations, and return a Db.
Configures the pool with:
PRAGMA foreign_keys = ON— FK constraint enforcementPRAGMA journal_mode = WAL— concurrent reads (silently ignored for:memory:)PRAGMA busy_timeout = 5000— wait under contention instead of immediate SQLITE_BUSY
Sourcepub fn pool(&self) -> &SqlitePool
pub fn pool(&self) -> &SqlitePool
Return a reference to the underlying connection pool.
Source§impl Db
impl Db
pub async fn create_email_verification( &self, user_id: UserId, ) -> Result<String, AuthError>
pub async fn verify_email(&self, raw_token: &str) -> Result<bool, AuthError>
pub async fn send_verification_email( &self, user_id: UserId, email: &Email, base_url: &str, sender: &dyn EmailSender, ) -> Result<(), AuthError>
Source§impl Db
impl Db
Sourcepub async fn create_invitation(
&self,
email: Option<&Email>,
metadata: Option<&str>,
invited_by: Option<UserId>,
expires_at: DateTime<Utc>,
) -> Result<(String, Invitation), AuthError>
pub async fn create_invitation( &self, email: Option<&Email>, metadata: Option<&str>, invited_by: Option<UserId>, expires_at: DateTime<Utc>, ) -> Result<(String, Invitation), AuthError>
Create an invitation. Returns the raw token (shown once) and the
Invitation record.
If email is Some, the invitation is targeted at that address.
If email is None, it is an open invitation usable by anyone.
Sourcepub async fn consume_invitation(
&self,
id: InvitationId,
) -> Result<(), AuthError>
pub async fn consume_invitation( &self, id: InvitationId, ) -> Result<(), AuthError>
Mark an invitation as consumed.
Uses consumed_at IS NULL as a concurrency guard. Returns Ok(())
on success. Returns Err(AuthError::NotFound) if the ID does not
exist. Returns Err(AuthError::Gone) if already consumed — the
caller should treat this as a race loss.
Sourcepub async fn list_pending_invitations(
&self,
) -> Result<Vec<Invitation>, AuthError>
pub async fn list_pending_invitations( &self, ) -> Result<Vec<Invitation>, AuthError>
List unconsumed, non-expired invitations, newest first.
Sourcepub async fn delete_invitation(&self, id: InvitationId) -> Result<(), AuthError>
pub async fn delete_invitation(&self, id: InvitationId) -> Result<(), AuthError>
Delete an invitation outright, whether pending or consumed.
Sourcepub async fn validate_invitation(
&self,
raw_token: &str,
) -> Result<Option<Invitation>, AuthError>
pub async fn validate_invitation( &self, raw_token: &str, ) -> Result<Option<Invitation>, AuthError>
Validate a raw invitation token.
Returns Some(Invitation) if the token exists, is not expired, and has
not been consumed. Returns None otherwise. The caller is responsible
for checking email match on targeted invitations.
Source§impl Db
impl Db
Sourcepub async fn create_oauth_state(
&self,
provider: &str,
redirect_uri: &str,
pkce_verifier: &str,
post_login_redirect: Option<&str>,
linking_user_id: Option<UserId>,
) -> Result<String, AuthError>
pub async fn create_oauth_state( &self, provider: &str, redirect_uri: &str, pkce_verifier: &str, post_login_redirect: Option<&str>, linking_user_id: Option<UserId>, ) -> Result<String, AuthError>
Create an OAuth state record. Returns the raw state value (for the authorize URL).
linking_user_id is Some when initiating the account-linking flow (the user is
already authenticated and wants to add a provider). It is None for the standard
login/register flow.
Sourcepub async fn validate_oauth_state(
&self,
raw_state: &str,
) -> Result<Option<OAuthStateInfo>, AuthError>
pub async fn validate_oauth_state( &self, raw_state: &str, ) -> Result<Option<OAuthStateInfo>, AuthError>
Validate and consume an OAuth state. Returns the stored info or None if invalid/expired. Atomically deletes to prevent reuse.
Sourcepub async fn create_oauth_user(
&self,
email: Email,
provider: &str,
provider_user_id: &str,
) -> Result<User, AuthError>
pub async fn create_oauth_user( &self, email: Email, provider: &str, provider_user_id: &str, ) -> Result<User, AuthError>
Create a user via OAuth – no password.
Creates the user (password_hash = NULL) and the oauth_accounts row in a single transaction. Returns the created User.
Sourcepub async fn link_oauth_account(
&self,
user_id: UserId,
provider: &str,
provider_user_id: &str,
email: &str,
) -> Result<(), AuthError>
pub async fn link_oauth_account( &self, user_id: UserId, provider: &str, provider_user_id: &str, email: &str, ) -> Result<(), AuthError>
Link an OAuth identity to an existing user.
Sourcepub async fn find_user_by_oauth(
&self,
provider: &str,
provider_user_id: &str,
) -> Result<Option<User>, AuthError>
pub async fn find_user_by_oauth( &self, provider: &str, provider_user_id: &str, ) -> Result<Option<User>, AuthError>
Find an allowthem user by provider + provider_user_id.
Sourcepub async fn get_user_oauth_accounts(
&self,
user_id: UserId,
) -> Result<Vec<OAuthAccountInfo>, AuthError>
pub async fn get_user_oauth_accounts( &self, user_id: UserId, ) -> Result<Vec<OAuthAccountInfo>, AuthError>
List all OAuth accounts linked to a user.
Source§impl Db
impl Db
Sourcepub async fn create_password_reset(
&self,
email: &Email,
) -> Result<Option<String>, AuthError>
pub async fn create_password_reset( &self, email: &Email, ) -> Result<Option<String>, AuthError>
Create a password reset token for the user with the given email.
Looks up the user by email. If found, inserts a new reset token record
(hashed) and returns the raw token for inclusion in the reset URL.
Returns None if no user exists for that email (caller should not
reveal this to prevent email enumeration).
Sourcepub async fn validate_reset_token(
&self,
raw_token: &str,
) -> Result<Option<(ResetTokenId, UserId)>, AuthError>
pub async fn validate_reset_token( &self, raw_token: &str, ) -> Result<Option<(ResetTokenId, UserId)>, AuthError>
Validate a raw reset token.
Hashes the token and looks it up in the database. Returns the associated
UserId and token record ID if the token exists, has not expired, and has
not been used. Returns None if the token is invalid or expired.
Sourcepub async fn execute_reset(
&self,
raw_token: &str,
new_password: &str,
) -> Result<bool, AuthError>
pub async fn execute_reset( &self, raw_token: &str, new_password: &str, ) -> Result<bool, AuthError>
Execute a password reset: update the password and mark the token used.
Runs atomically in a transaction:
- Validate the token (not expired, not used).
- Mark the token as used (
used_at = now). - Hash the new password.
- Update the user’s
password_hashandupdated_at.
Returns Ok(true) on success, Ok(false) if the token was invalid.
Sourcepub async fn send_password_reset(
&self,
email: &Email,
base_url: &str,
sender: &dyn EmailSender,
) -> Result<(), AuthError>
pub async fn send_password_reset( &self, email: &Email, base_url: &str, sender: &dyn EmailSender, ) -> Result<(), AuthError>
Send a password reset email for the given address.
Calls create_password_reset to generate a token. If the email exists,
constructs a reset URL using base_url and delivers it via sender.
If the email does not exist, returns Ok(()) silently (no enumeration).
Source§impl Db
impl Db
Sourcepub async fn create_permission(
&self,
name: &PermissionName,
description: Option<&str>,
) -> Result<Permission, AuthError>
pub async fn create_permission( &self, name: &PermissionName, description: Option<&str>, ) -> Result<Permission, AuthError>
Create a permission with a unique name and optional description.
Sourcepub async fn get_permission(
&self,
id: &PermissionId,
) -> Result<Option<Permission>, AuthError>
pub async fn get_permission( &self, id: &PermissionId, ) -> Result<Option<Permission>, AuthError>
Get a permission by ID. Returns None if not found.
Sourcepub async fn get_permission_by_name(
&self,
name: &PermissionName,
) -> Result<Option<Permission>, AuthError>
pub async fn get_permission_by_name( &self, name: &PermissionName, ) -> Result<Option<Permission>, AuthError>
Get a permission by name. Returns None if not found.
Sourcepub async fn list_permissions(&self) -> Result<Vec<Permission>, AuthError>
pub async fn list_permissions(&self) -> Result<Vec<Permission>, AuthError>
List all permissions, ordered by creation time.
Sourcepub async fn delete_permission(
&self,
id: &PermissionId,
) -> Result<bool, AuthError>
pub async fn delete_permission( &self, id: &PermissionId, ) -> Result<bool, AuthError>
Delete a permission by ID. Returns true if a row was deleted, false if not found.
Cascades to allowthem_role_permissions and allowthem_user_permissions.
Sourcepub async fn assign_permission_to_role(
&self,
role_id: &RoleId,
permission_id: &PermissionId,
) -> Result<(), AuthError>
pub async fn assign_permission_to_role( &self, role_id: &RoleId, permission_id: &PermissionId, ) -> Result<(), AuthError>
Assign a permission to a role. Idempotent — silently succeeds if already assigned.
Sourcepub async fn assign_permission_to_user(
&self,
user_id: &UserId,
permission_id: &PermissionId,
) -> Result<(), AuthError>
pub async fn assign_permission_to_user( &self, user_id: &UserId, permission_id: &PermissionId, ) -> Result<(), AuthError>
Assign a permission directly to a user. Idempotent — silently succeeds if already assigned.
Sourcepub async fn unassign_permission_from_role(
&self,
role_id: &RoleId,
permission_id: &PermissionId,
) -> Result<bool, AuthError>
pub async fn unassign_permission_from_role( &self, role_id: &RoleId, permission_id: &PermissionId, ) -> Result<bool, AuthError>
Unassign a permission from a role. Returns true if removed, false if not found.
Sourcepub async fn unassign_permission_from_user(
&self,
user_id: &UserId,
permission_id: &PermissionId,
) -> Result<bool, AuthError>
pub async fn unassign_permission_from_user( &self, user_id: &UserId, permission_id: &PermissionId, ) -> Result<bool, AuthError>
Unassign a permission from a user. Returns true if removed, false if not found.
Sourcepub async fn has_permission(
&self,
user_id: &UserId,
perm_name: &PermissionName,
) -> Result<bool, AuthError>
pub async fn has_permission( &self, user_id: &UserId, perm_name: &PermissionName, ) -> Result<bool, AuthError>
Check whether a user has a permission by name via either path: direct user assignment or any of the user’s roles.
Sourcepub async fn get_user_permissions(
&self,
user_id: &UserId,
) -> Result<Vec<Permission>, AuthError>
pub async fn get_user_permissions( &self, user_id: &UserId, ) -> Result<Vec<Permission>, AuthError>
Return all permissions for a user — both directly assigned and via roles — deduplicated and ordered by name.
Source§impl Db
impl Db
Sourcepub async fn create_role(
&self,
name: &RoleName,
description: Option<&str>,
) -> Result<Role, AuthError>
pub async fn create_role( &self, name: &RoleName, description: Option<&str>, ) -> Result<Role, AuthError>
Create a role with a unique name and optional description.
Sourcepub async fn get_role(&self, id: &RoleId) -> Result<Option<Role>, AuthError>
pub async fn get_role(&self, id: &RoleId) -> Result<Option<Role>, AuthError>
Get a role by ID. Returns None if not found.
Sourcepub async fn get_role_by_name(
&self,
name: &RoleName,
) -> Result<Option<Role>, AuthError>
pub async fn get_role_by_name( &self, name: &RoleName, ) -> Result<Option<Role>, AuthError>
Get a role by name. Returns None if not found.
Sourcepub async fn list_roles(&self) -> Result<Vec<Role>, AuthError>
pub async fn list_roles(&self) -> Result<Vec<Role>, AuthError>
List all roles, ordered by creation time.
Sourcepub async fn delete_role(&self, id: &RoleId) -> Result<bool, AuthError>
pub async fn delete_role(&self, id: &RoleId) -> Result<bool, AuthError>
Delete a role by ID. Returns true if a row was deleted, false if not found.
Cascades to allowthem_user_roles and allowthem_role_permissions.
Sourcepub async fn assign_role(
&self,
user_id: &UserId,
role_id: &RoleId,
) -> Result<(), AuthError>
pub async fn assign_role( &self, user_id: &UserId, role_id: &RoleId, ) -> Result<(), AuthError>
Assign a role to a user. Silently succeeds if already assigned (idempotent).
Sourcepub async fn unassign_role(
&self,
user_id: &UserId,
role_id: &RoleId,
) -> Result<bool, AuthError>
pub async fn unassign_role( &self, user_id: &UserId, role_id: &RoleId, ) -> Result<bool, AuthError>
Unassign a role from a user. Returns true if removed, false if the assignment
did not exist.
Sourcepub async fn has_role(
&self,
user_id: &UserId,
role_name: &RoleName,
) -> Result<bool, AuthError>
pub async fn has_role( &self, user_id: &UserId, role_name: &RoleName, ) -> Result<bool, AuthError>
Check whether a user has a specific role by name.
Sourcepub async fn get_user_roles(
&self,
user_id: &UserId,
) -> Result<Vec<Role>, AuthError>
pub async fn get_user_roles( &self, user_id: &UserId, ) -> Result<Vec<Role>, AuthError>
Return all roles assigned to a user, ordered by creation time.
Sourcepub async fn bootstrap_roles(
&self,
names: &[&str],
) -> Result<Vec<Role>, AuthError>
pub async fn bootstrap_roles( &self, names: &[&str], ) -> Result<Vec<Role>, AuthError>
Create each named role if it does not already exist.
Returns roles in the same order as names. Idempotent: existing roles
are fetched, not re-created. Duplicates within names are allowed; each
name is resolved independently.
Sourcepub async fn resolve_highest_role(
&self,
user_id: &UserId,
hierarchy: &[&str],
) -> Result<Option<String>, AuthError>
pub async fn resolve_highest_role( &self, user_id: &UserId, hierarchy: &[&str], ) -> Result<Option<String>, AuthError>
Return the name of the first role in hierarchy that the user holds.
hierarchy[0] is treated as the highest role. Returns None if the user
holds none of the listed roles. An empty hierarchy always returns None.
Source§impl Db
impl Db
Sourcepub async fn create_session(
&self,
user_id: UserId,
token_hash: TokenHash,
ip_address: Option<&str>,
user_agent: Option<&str>,
expires_at: DateTime<Utc>,
) -> Result<Session, AuthError>
pub async fn create_session( &self, user_id: UserId, token_hash: TokenHash, ip_address: Option<&str>, user_agent: Option<&str>, expires_at: DateTime<Utc>, ) -> Result<Session, AuthError>
Insert a new session record and return it.
The caller is responsible for hashing the token before calling this function
via hash_token(). The raw token must never be passed here.
Sourcepub async fn lookup_session(
&self,
token: &SessionToken,
) -> Result<Option<Session>, AuthError>
pub async fn lookup_session( &self, token: &SessionToken, ) -> Result<Option<Session>, AuthError>
Look up a session by raw token.
Hashes the token internally and queries by hash. Expired sessions
(where expires_at is in the past) are excluded. Returns None
when no matching active session is found.
Sourcepub async fn validate_session(
&self,
token: &SessionToken,
ttl: Duration,
) -> Result<Option<Session>, AuthError>
pub async fn validate_session( &self, token: &SessionToken, ttl: Duration, ) -> Result<Option<Session>, AuthError>
Validate a session token and optionally extend it.
Fetches the active session by token hash. If the session is past the
halfway point of its TTL (now > expires_at - ttl/2), it is renewed
by setting expires_at = now + ttl. Returns the session with the
updated expiry, or None if no active session was found.
Sourcepub async fn delete_session(
&self,
token: &SessionToken,
) -> Result<bool, AuthError>
pub async fn delete_session( &self, token: &SessionToken, ) -> Result<bool, AuthError>
Delete a single session by raw token.
Returns true if a session was found and deleted, false if no
matching session existed.
Sourcepub async fn delete_user_sessions(
&self,
user_id: &UserId,
) -> Result<u64, AuthError>
pub async fn delete_user_sessions( &self, user_id: &UserId, ) -> Result<u64, AuthError>
Delete all sessions for a user.
Returns the number of sessions that were deleted.
Sourcepub async fn list_user_sessions(
&self,
user_id: UserId,
) -> Result<Vec<Session>, AuthError>
pub async fn list_user_sessions( &self, user_id: UserId, ) -> Result<Vec<Session>, AuthError>
List all active (non-expired) sessions for a user.
Returns sessions ordered by created_at descending (newest first).
Sourcepub async fn list_all_sessions(
&self,
params: ListSessionsParams,
) -> Result<ListSessionsResult, AuthError>
pub async fn list_all_sessions( &self, params: ListSessionsParams, ) -> Result<ListSessionsResult, AuthError>
List all active sessions with user email, for admin session viewer.
Joins sessions with users. Filters to non-expired sessions only. Optional user_id filter. Two static query variants (no dynamic SQL).
Source§impl Db
impl Db
Sourcepub async fn create_signing_key(
&self,
encryption_key: &[u8; 32],
) -> Result<SigningKey, AuthError>
pub async fn create_signing_key( &self, encryption_key: &[u8; 32], ) -> Result<SigningKey, AuthError>
Generate an RS256 key pair, encrypt the private key, and store both in the database.
The new key is NOT automatically marked active — call activate_signing_key separately.
Sourcepub async fn activate_signing_key(
&self,
key_id: SigningKeyId,
) -> Result<(), AuthError>
pub async fn activate_signing_key( &self, key_id: SigningKeyId, ) -> Result<(), AuthError>
Mark a key as the active signing key. Deactivates all other keys in a single transaction.
Returns AuthError::NotFound if the key ID does not exist.
Sourcepub async fn rotate_signing_key(
&self,
encryption_key: &[u8; 32],
) -> Result<SigningKey, AuthError>
pub async fn rotate_signing_key( &self, encryption_key: &[u8; 32], ) -> Result<SigningKey, AuthError>
Generate a new key and activate it, deactivating the current active key.
Combines creation and activation in a single transaction.
Sourcepub async fn get_active_signing_key(&self) -> Result<SigningKey, AuthError>
pub async fn get_active_signing_key(&self) -> Result<SigningKey, AuthError>
Get the currently active signing key.
Returns AuthError::NotFound if no key is active.
Sourcepub async fn get_all_signing_keys(&self) -> Result<Vec<SigningKey>, AuthError>
pub async fn get_all_signing_keys(&self) -> Result<Vec<SigningKey>, AuthError>
Get all signing keys ordered by creation date descending.
Used by the JWKS endpoint to serve all public keys (active + rotated-out).
Sourcepub async fn get_signing_key(
&self,
id: SigningKeyId,
) -> Result<SigningKey, AuthError>
pub async fn get_signing_key( &self, id: SigningKeyId, ) -> Result<SigningKey, AuthError>
Get a specific signing key by ID.
Returns AuthError::NotFound if no key matches the ID.
Source§impl Db
impl Db
Sourcepub async fn create_refresh_token(
&self,
application_id: ApplicationId,
user_id: UserId,
token_hash: &TokenHash,
scopes: &[String],
authorization_code_id: Option<AuthorizationCodeId>,
) -> Result<RefreshToken, AuthError>
pub async fn create_refresh_token( &self, application_id: ApplicationId, user_id: UserId, token_hash: &TokenHash, scopes: &[String], authorization_code_id: Option<AuthorizationCodeId>, ) -> Result<RefreshToken, AuthError>
Create a refresh token record. Expires after 30 days.
Sourcepub async fn revoke_refresh_tokens_by_auth_code(
&self,
authorization_code_id: AuthorizationCodeId,
) -> Result<u64, AuthError>
pub async fn revoke_refresh_tokens_by_auth_code( &self, authorization_code_id: AuthorizationCodeId, ) -> Result<u64, AuthError>
Revoke all refresh tokens issued from a specific authorization code.
Used for code-reuse detection (RFC 6749 Section 10.5).
Sourcepub async fn get_refresh_token_by_hash(
&self,
token_hash: &TokenHash,
) -> Result<Option<RefreshToken>, AuthError>
pub async fn get_refresh_token_by_hash( &self, token_hash: &TokenHash, ) -> Result<Option<RefreshToken>, AuthError>
Look up a refresh token by its SHA-256 hash.
Returns Ok(None) if no token matches. The caller must hash
the raw token before calling this method.
Sourcepub async fn revoke_refresh_token(
&self,
id: RefreshTokenId,
) -> Result<(), AuthError>
pub async fn revoke_refresh_token( &self, id: RefreshTokenId, ) -> Result<(), AuthError>
Revoke a single refresh token by setting revoked_at to now.
Used during token rotation: the old refresh token is revoked before the new one is issued.
Source§impl Db
impl Db
Sourcepub async fn get_pending_mfa_secret(
&self,
user_id: UserId,
mfa_key: &[u8; 32],
) -> Result<Option<String>, AuthError>
pub async fn get_pending_mfa_secret( &self, user_id: UserId, mfa_key: &[u8; 32], ) -> Result<Option<String>, AuthError>
Retrieve a pending (non-enabled) MFA secret for a user.
Returns Some(base32_secret) if a non-enabled secret exists, None otherwise.
Used by the setup page to avoid regenerating the secret on every page load.
Sourcepub async fn create_mfa_secret(
&self,
user_id: UserId,
mfa_key: &[u8; 32],
) -> Result<String, AuthError>
pub async fn create_mfa_secret( &self, user_id: UserId, mfa_key: &[u8; 32], ) -> Result<String, AuthError>
Generate a new TOTP secret for a user and store it (encrypted, not yet enabled).
Returns the plaintext base32-encoded secret for display to the user
during the setup flow. The caller must present this secret (or a QR code
derived from it) and require the user to confirm with a valid code
before calling enable_mfa.
Fails with MfaAlreadyEnabled if the user already has an enabled MFA secret.
If a non-enabled secret exists (abandoned setup attempt), it is replaced.
Sourcepub async fn enable_mfa(
&self,
user_id: UserId,
code: &str,
mfa_key: &[u8; 32],
) -> Result<Vec<String>, AuthError>
pub async fn enable_mfa( &self, user_id: UserId, code: &str, mfa_key: &[u8; 32], ) -> Result<Vec<String>, AuthError>
Enable MFA for a user after verifying a TOTP code.
Decrypts the stored secret, validates the provided code against it,
and if valid, sets enabled = 1 and inserts 10 hashed recovery codes.
Returns the plaintext recovery codes (this is the only time they are visible).
Runs in a transaction to ensure MFA is never enabled without recovery codes.
Sourcepub async fn verify_totp(
&self,
user_id: UserId,
code: &str,
mfa_key: &[u8; 32],
) -> Result<bool, AuthError>
pub async fn verify_totp( &self, user_id: UserId, code: &str, mfa_key: &[u8; 32], ) -> Result<bool, AuthError>
Validate a TOTP code against a user’s enabled MFA secret.
Returns Ok(true) if the code is valid, Ok(false) if invalid.
Returns Err(MfaNotEnabled) if the user has no enabled MFA.
Sourcepub async fn has_mfa_enabled(&self, user_id: UserId) -> Result<bool, AuthError>
pub async fn has_mfa_enabled(&self, user_id: UserId) -> Result<bool, AuthError>
Check whether a user has MFA enabled.
Sourcepub async fn disable_mfa(&self, user_id: UserId) -> Result<(), AuthError>
pub async fn disable_mfa(&self, user_id: UserId) -> Result<(), AuthError>
Disable MFA for a user. Deletes the secret and all recovery codes.
Uses a transaction to ensure both deletes are atomic.
Sourcepub async fn verify_recovery_code(
&self,
user_id: UserId,
code: &str,
) -> Result<bool, AuthError>
pub async fn verify_recovery_code( &self, user_id: UserId, code: &str, ) -> Result<bool, AuthError>
Verify a recovery code. If valid, marks it as used (one-time use).
Uses atomic UPDATE ... RETURNING to prevent race conditions.
Returns Ok(true) if the code was valid and consumed,
Ok(false) if no matching unused code was found.
Sourcepub async fn remaining_recovery_codes(
&self,
user_id: UserId,
) -> Result<i64, AuthError>
pub async fn remaining_recovery_codes( &self, user_id: UserId, ) -> Result<i64, AuthError>
Count remaining unused recovery codes for a user.
Sourcepub async fn regenerate_recovery_codes(
&self,
user_id: UserId,
) -> Result<Vec<String>, AuthError>
pub async fn regenerate_recovery_codes( &self, user_id: UserId, ) -> Result<Vec<String>, AuthError>
Replace all recovery codes with a fresh set of 10.
Deletes all existing codes (used and unused) and inserts 10 new ones. Returns the plaintext codes. Runs in a transaction.
Sourcepub async fn create_mfa_challenge(
&self,
user_id: UserId,
) -> Result<String, AuthError>
pub async fn create_mfa_challenge( &self, user_id: UserId, ) -> Result<String, AuthError>
Create a short-lived MFA challenge token after password verification.
The integrator calls this when a user with MFA enabled passes password verification. Returns the raw token string to send to the client. The client presents this token along with a TOTP code to complete login. Challenge tokens expire after 5 minutes.
Sourcepub async fn validate_mfa_challenge(
&self,
raw_token: &str,
) -> Result<Option<UserId>, AuthError>
pub async fn validate_mfa_challenge( &self, raw_token: &str, ) -> Result<Option<UserId>, AuthError>
Validate an MFA challenge token without consuming it.
Returns Some(user_id) if the token is valid and not expired,
None otherwise. Does not consume the token so the user can retry
if they mistype the TOTP code.
Source§impl Db
impl Db
Sourcepub async fn create_user(
&self,
email: Email,
password: &str,
username: Option<Username>,
custom_data: Option<&Value>,
) -> Result<User, AuthError>
pub async fn create_user( &self, email: Email, password: &str, username: Option<Username>, custom_data: Option<&Value>, ) -> Result<User, AuthError>
Create a user with email, plaintext password, optional username, and optional custom data.
Hashes the password with Argon2id (via password::hash_password).
Returns the created User (without password_hash in the returned struct).
Sourcepub async fn create_user_with_hash(
&self,
email: Email,
password_hash: &str,
username: Option<Username>,
custom_data: Option<&Value>,
) -> Result<User, AuthError>
pub async fn create_user_with_hash( &self, email: Email, password_hash: &str, username: Option<Username>, custom_data: Option<&Value>, ) -> Result<User, AuthError>
Import a user with a pre-existing password hash (for migration from external systems). The hash must be a valid Argon2 PHC string. No validation is performed on it.
Sourcepub async fn get_user(&self, id: UserId) -> Result<User, AuthError>
pub async fn get_user(&self, id: UserId) -> Result<User, AuthError>
Look up a user by ID. Returns User with password_hash = None.
Sourcepub async fn get_user_by_email(&self, email: &Email) -> Result<User, AuthError>
pub async fn get_user_by_email(&self, email: &Email) -> Result<User, AuthError>
Look up a user by email. Returns User with password_hash = None.
Sourcepub async fn get_user_by_username(
&self,
username: &Username,
) -> Result<User, AuthError>
pub async fn get_user_by_username( &self, username: &Username, ) -> Result<User, AuthError>
Look up a user by username. Returns User with password_hash = None.
Sourcepub async fn find_for_login(&self, identifier: &str) -> Result<User, AuthError>
pub async fn find_for_login(&self, identifier: &str) -> Result<User, AuthError>
Look up a user by email OR username for login.
Returns User WITH password_hash populated. The caller is responsible
for calling verify_password() to check the password.
Sourcepub async fn update_user_email(
&self,
id: UserId,
email: Email,
) -> Result<(), AuthError>
pub async fn update_user_email( &self, id: UserId, email: Email, ) -> Result<(), AuthError>
Update a user’s email. Also updates updated_at.
Sourcepub async fn update_user_username(
&self,
id: UserId,
username: Option<Username>,
) -> Result<(), AuthError>
pub async fn update_user_username( &self, id: UserId, username: Option<Username>, ) -> Result<(), AuthError>
Update a user’s username (set or clear). Also updates updated_at.
Sourcepub async fn update_user_active(
&self,
id: UserId,
is_active: bool,
) -> Result<(), AuthError>
pub async fn update_user_active( &self, id: UserId, is_active: bool, ) -> Result<(), AuthError>
Update a user’s is_active flag. Also updates updated_at.
Sourcepub async fn delete_user(&self, id: UserId) -> Result<(), AuthError>
pub async fn delete_user(&self, id: UserId) -> Result<(), AuthError>
Delete a user by ID. Cascades to sessions, user_roles, user_permissions.
Sourcepub async fn list_users(&self) -> Result<Vec<User>, AuthError>
pub async fn list_users(&self) -> Result<Vec<User>, AuthError>
List all users ordered by created_at ASC. Returns User with password_hash = None.
Sourcepub async fn search_users(
&self,
params: SearchUsersParams<'_>,
) -> Result<SearchUsersResult, AuthError>
pub async fn search_users( &self, params: SearchUsersParams<'_>, ) -> Result<SearchUsersResult, AuthError>
Search and filter users with pagination.
Builds a dynamic query with optional search term (matched against email and username via LIKE), status filter, and MFA filter. Returns matching users with their MFA enrollment status.
Sourcepub async fn update_user_password(
&self,
id: UserId,
new_password: &str,
) -> Result<(), AuthError>
pub async fn update_user_password( &self, id: UserId, new_password: &str, ) -> Result<(), AuthError>
Update a user’s password. Hashes new_password with Argon2id and stores it.
Returns AuthError::NotFound if no user with id exists.
Sourcepub async fn clear_password_hash(&self, id: UserId) -> Result<(), AuthError>
pub async fn clear_password_hash(&self, id: UserId) -> Result<(), AuthError>
Set a user’s password hash to NULL.
Used by admin force-password-reset to invalidate the current password.
The login flow falls back to a dummy hash when password_hash is NULL,
so verify_password will always fail.
Sourcepub async fn get_custom_data(
&self,
id: &UserId,
) -> Result<Option<Value>, AuthError>
pub async fn get_custom_data( &self, id: &UserId, ) -> Result<Option<Value>, AuthError>
Get a user’s custom data.
Returns Err(NotFound) if no user with id exists.
Returns Ok(None) if the user exists but has no custom data.
Auto Trait Implementations§
impl Freeze for Db
impl !RefUnwindSafe for Db
impl Send for Db
impl Sync for Db
impl Unpin for Db
impl UnsafeUnpin for Db
impl !UnwindSafe for Db
Blanket Implementations§
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
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> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more