pub struct JwtSessionService { /* private fields */ }Expand description
Stateful JWT session service.
Manages the full lifecycle of JWT-based sessions backed by a SQLite session
table. Each session is represented as a database row — the jti claim in
both the access and refresh tokens contains the hex-encoded session token,
which is hashed before storage.
Cloning is cheap — all state is behind Arc.
§Session lifecycle
authenticate— creates a new session row, issues an access + refresh token pair.rotate— validates the refresh token, rotates the stored token hash, issues a new pair.logout— validates the access token, destroys the session row.
§Wiring
let config = JwtSessionsConfig::new("my-super-secret-key-for-signing-tokens");
let svc = JwtSessionService::new(db, config)?;
// Authenticate a user (e.g. after password check)
let meta = SessionMeta::from_headers(ip, user_agent, accept_language, accept_encoding);
let pair = svc.authenticate("user_123", &meta).await?;
// Rotate (called from the refresh endpoint)
let new_pair = svc.rotate(&pair.refresh_token).await?;
// Logout (called from the logout endpoint)
svc.logout(&pair.access_token).await?;Implementations§
Source§impl JwtSessionService
impl JwtSessionService
Sourcepub fn new(db: Database, config: JwtSessionsConfig) -> Result<Self>
pub fn new(db: Database, config: JwtSessionsConfig) -> Result<Self>
Create a new JwtSessionService.
Validates that signing_secret is non-empty and builds the encoder,
decoder, and session store. Returns an error immediately if the config
is invalid — fail fast at startup.
§Errors
Returns Error::internal if signing_secret is empty.
Sourcepub fn encoder(&self) -> &JwtEncoder
pub fn encoder(&self) -> &JwtEncoder
Returns a reference to the JWT encoder.
Sourcepub fn decoder(&self) -> &JwtDecoder
pub fn decoder(&self) -> &JwtDecoder
Returns a reference to the JWT decoder.
Sourcepub fn config(&self) -> &JwtSessionsConfig
pub fn config(&self) -> &JwtSessionsConfig
Returns a reference to the service configuration.
Sourcepub fn layer(&self) -> JwtLayer
pub fn layer(&self) -> JwtLayer
Creates a JwtLayer backed by this service.
The returned layer performs stateful validation: after verifying the JWT
signature and claims it hashes the jti, loads the session row, and
inserts the transport-agnostic Session
into request extensions. Returns 401 when the session row is absent.
§Example
let svc = JwtSessionService::new(db, config)?;
let app = Router::new()
.route("/me", get(whoami).route_layer(svc.layer()));Sourcepub async fn authenticate(
&self,
user_id: &str,
meta: &SessionMeta,
) -> Result<TokenPair>
pub async fn authenticate( &self, user_id: &str, meta: &SessionMeta, ) -> Result<TokenPair>
Authenticate a user and issue a new TokenPair.
Creates a session row in the database. The access and refresh tokens
both carry the session token hex as the jti claim. The access token
audience is "access"; the refresh token audience is "refresh".
§Errors
Returns an error if the session row cannot be created or the tokens cannot be signed.
Sourcepub async fn rotate(&self, refresh_token: &str) -> Result<TokenPair>
pub async fn rotate(&self, refresh_token: &str) -> Result<TokenPair>
Rotate a refresh token, issuing a new TokenPair.
Validates the provided refresh_token (signature, expiry, audience),
then rotates the stored session token hash and mints a fresh pair. The
old refresh token is immediately invalidated — a second call with the
same token returns auth:session_not_found.
§Errors
auth:aud_mismatch— the token has the wrong audience (e.g. an access token was passed).auth:session_not_found— the session row does not exist or has expired.- JWT validation errors (
jwt:*) — expired, tampered, etc.
Sourcepub async fn logout(&self, access_token: &str) -> Result<()>
pub async fn logout(&self, access_token: &str) -> Result<()>
Revoke the session associated with an access token.
Validates the access_token (signature, expiry, audience), then destroys
the session row. If the session is already gone (e.g. concurrent logout),
the call is a no-op and succeeds.
§Errors
auth:aud_mismatch— a refresh token was passed instead of an access token.- JWT validation errors (
jwt:*) — expired, tampered, etc.
Sourcepub async fn revoke(&self, user_id: &str, id: &str) -> Result<()>
pub async fn revoke(&self, user_id: &str, id: &str) -> Result<()>
Revoke a specific session by its ULID identifier.
Looks up the session row by id, verifies that it belongs to user_id,
and destroys it. Returns 404 auth:session_not_found if the session does
not exist or belongs to a different user.
§Errors
Returns 404 auth:session_not_found on ownership mismatch, or an
internal error if the database operation fails.
Sourcepub async fn revoke_all(&self, user_id: &str) -> Result<()>
pub async fn revoke_all(&self, user_id: &str) -> Result<()>
Sourcepub async fn revoke_all_except(
&self,
user_id: &str,
keep_id: &str,
) -> Result<()>
pub async fn revoke_all_except( &self, user_id: &str, keep_id: &str, ) -> Result<()>
Revoke all sessions for the given user except the session with keep_id.
Used to implement “log out other devices”.
§Errors
Returns an error if the database delete fails.
Sourcepub async fn cleanup_expired(&self) -> Result<u64>
pub async fn cleanup_expired(&self) -> Result<u64>
Delete all expired sessions from the database.
Returns the number of rows deleted. Schedule periodically (e.g. via a cron job) to keep the table small.
§Errors
Returns an error if the database delete fails.
Trait Implementations§
Source§impl Clone for JwtSessionService
impl Clone for JwtSessionService
Source§fn clone(&self) -> JwtSessionService
fn clone(&self) -> JwtSessionService
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more