Skip to content

Sessions

sign_in()
→ generate 32 random bytes (raw token)
→ hex-encode (64-char string — sent to client once)
→ SHA-256 hash (stored in DB)
→ set HttpOnly cookie
→ return raw token in response body
subsequent requests
→ read cookie value (raw token)
→ SHA-256 hash
→ look up in sessions table
→ return Identity if found and not expired
sign_out()
→ SHA-256 hash cookie value
→ delete session row
→ clear cookie
pub struct Session {
pub id: Uuid,
pub user_id: Uuid,
pub token_hash: String, // SHA-256 of raw token
pub device_info: serde_json::Value,
pub ip_address: String,
pub org_id: Option<Uuid>, // active organization context
pub expires_at: DateTime<Utc>,
pub created_at: DateTime<Utc>,
}
  • Raw tokens are never stored — only their SHA-256 hash
  • If the database is compromised, attackers cannot reuse tokens without inverting SHA-256
  • Tokens are 32 random bytes = 256 bits of entropy — brute force is infeasible

SessionLayer sets an authx_session cookie with:

FlagValuePurpose
HttpOnlytrueJS cannot read the token
SameSiteLaxCSRF mitigation for cross-site requests
SecureconfigurableHTTPS-only in production
Path/Available on all routes
Max-Agesession TTLAuto-expires in browser

Users can have multiple concurrent sessions (one per device). sign_out_all() invalidates all of them atomically.

A session carries org_id — the currently active organization. Switch it with:

org_svc.switch_org(session_id, target_org_id, user_id).await?;

The next request after the session update sees the new identity.active_org.