Auth & Session Model
Token Types
| Token | Length | Storage | Expiry | Purpose |
|---|---|---|---|---|
| Session token | 64-char alphanumeric | sessions.session_token | 7 days | Bearer auth on every request |
| Verification token | 32-char alphanumeric | verification_tokens.token | 24 hours | Email verification link |
| OTP | 6-digit numeric | verification_tokens.otp | 24 hours | Email verification code |
| Password reset token | 32-char alphanumeric | password_reset_tokens.token | 24 hours | Password reset link |
All tokens are generated via rand::thread_rng() — cryptographically secure random generation.
Auth State Lifecycle
Signup Flow
Password Requirements
Enforced in common/validation.rs:
- Minimum 8 characters, maximum 128 characters
- Must contain at least one uppercase letter
- Must contain at least one lowercase letter
- Must contain at least one digit
pub fn validate_password(password: &str) -> Result<(), ValidationError> {
if password.len() < 8 || password.len() > 128 { return Err(...) }
if !password.chars().any(|c| c.is_uppercase()) { return Err(...) }
if !password.chars().any(|c| c.is_lowercase()) { return Err(...) }
if !password.chars().any(|c| c.is_numeric()) { return Err(...) }
Ok(())
}Session Management
Session creation (auth/session.rs):
pub async fn create_session(db: &PgPool, user_id: Uuid, role: Role, ip: Option<IpNet>, user_agent: Option<String>) -> Result<Session>
// Generates 64-char token
// INSERT INTO sessions (user_id, session_token, expires_at=NOW()+7days, role, ip_address, user_agent)Session validation (in auth_middleware):
SELECT s.user_id, s.role FROM sessions s WHERE s.session_token = $1 AND s.expires_at > NOW()Session cleanup (auth/background.rs):
pub fn start_session_cleanup_task(db: PgPool) -> tokio::task::JoinHandle<()>
// Runs in background Tokio task
// DELETE FROM sessions WHERE expires_at < NOW()
// Interval: configurable, runs indefinitelyOAuth Flow
Role Model
#[derive(sqlx::Type)]
#[sqlx(type_name = "user_role", rename_all = "lowercase")]
pub enum Role {
User,
Admin,
Contributor,
}The user_role PostgreSQL enum has three variants: user, contributor, and admin.
Role propagation: admin role changes via PATCH /admin/users/{id}/role do not update active sessions. Users keep their previous session role until re-login or session expiry.
Exception: profile self-opt-in (PATCH /user/update-profile with contributor_opt_in=true) performs one-way user -> contributor escalation and synchronizes active sessions.role rows for that user.
Last updated on