Skip to content

Secure Authentication Systems in Rust: JWT, OAuth2, and Multi-Factor Authentication

Published: at 10:30 AM

Secure Authentication Systems in Rust

Introduction

Authentication forms the cornerstone of any secure system, yet it’s often implemented with critical vulnerabilities that expose entire infrastructures to compromise. In this comprehensive guide, we’ll build a production-ready authentication system in Rust that implements industry best practices including JWT tokens, OAuth2 integration, multi-factor authentication, and role-based access control.

Our system will demonstrate how Rust’s type safety and memory safety features help prevent common authentication vulnerabilities while delivering the performance and reliability required for enterprise-scale deployments.

Architecture Overview

Security-First Design

┌─────────────────────────────────────────────────────────────────┐
Client Applications
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────────┐  │
│  │   Web App   │  │ Mobile App  │  │    API Clients          │  │
│  └─────────────┘  └─────────────┘  └─────────────────────────┘  │
├─────────────────────────────────────────────────────────────────┤
API Gateway & Load Balancer
├─────────────────────────────────────────────────────────────────┤
Authentication Service
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────────┐  │
│  │    JWT      │  │   OAuth2    │  │        MFA              │  │
│  │  Manager    │  │  Provider   │  │     Manager             │  │
│  └─────────────┘  └─────────────┘  └─────────────────────────┘  │
├─────────────────────────────────────────────────────────────────┤
Authorization Layer
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────────┐  │
│  │    RBAC     │  │ Permissions │  │    Policy Engine        │  │
│  │   Engine    │  │  Manager    │  │                         │  │
│  └─────────────┘  └─────────────┘  └─────────────────────────┘  │
├─────────────────────────────────────────────────────────────────┤
Data Layer
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────────┐  │
│  │    User     │  │   Session   │  │      Audit Log          │  │
│  │  Database   │  │   Store     │  │                         │  │
│  └─────────────┘  └─────────────┘  └─────────────────────────┘  │
└─────────────────────────────────────────────────────────────────┘

Core Components

// Core authentication system architecture
pub struct AuthenticationSystem {
    jwt_manager: Arc<JwtManager>,
    oauth2_provider: Arc<OAuth2Provider>,
    mfa_manager: Arc<MfaManager>,
    rbac_engine: Arc<RbacEngine>,
    user_service: Arc<UserService>,
    session_store: Arc<SessionStore>,
    audit_logger: Arc<AuditLogger>,
    rate_limiter: Arc<RateLimiter>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuthenticationRequest {
    pub username: String,
    pub password: SecureString,
    pub mfa_token: Option<String>,
    pub client_info: ClientInfo,
    pub requested_scopes: Vec<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuthenticationResponse {
    pub access_token: String,
    pub refresh_token: String,
    pub token_type: String,
    pub expires_in: u64,
    pub scope: String,
    pub user_info: UserInfo,
}

Project Setup

Dependencies

# Cargo.toml
[package]
name = "secure-auth-system"
version = "1.0.0"
edition = "2021"

[dependencies]
# Web framework
axum = { version = "0.7", features = ["macros", "multipart"] }
tower = { version = "0.4", features = ["full"] }
tower-http = { version = "0.5", features = ["auth", "cors", "trace", "rate-limit"] }
hyper = { version = "1.0", features = ["full"] }

# Async runtime
tokio = { version = "1.35", features = ["full"] }
tokio-util = { version = "0.7", features = ["codec"] }

# Serialization
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
serde_urlencoded = "0.7"

# HTTP client
reqwest = { version = "0.11", features = ["json", "rustls-tls"] }

# Cryptography
ring = "0.17"
rustls = "0.22"
rustls-webpki = "0.102"
password-hash = "0.5"
argon2 = "0.5"
hmac = "0.12"
sha2 = "0.10"
aes-gcm = "0.10"
rand = "0.8"
rand_core = { version = "0.6", features = ["std"] }

# JWT
jsonwebtoken = "9.2"
base64 = "0.21"

# Database
sqlx = { version = "0.7", features = [
    "runtime-tokio-rustls",
    "postgres",
    "chrono",
    "uuid",
    "json"
] }
redis = { version = "0.24", features = ["tokio-comp", "connection-manager"] }

# Utilities
uuid = { version = "1.6", features = ["v4", "serde"] }
chrono = { version = "0.4", features = ["serde"] }
anyhow = "1.0"
thiserror = "1.0"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] }

# OAuth2
oauth2 = "4.4"
url = "2.5"

# TOTP for MFA
totp-rs = { version = "5.4", features = ["qr"] }
qrcode = "0.14"
image = "0.24"

# Rate limiting
governor = "0.6"
nonzero_ext = "0.3"

# Security
secrecy = { version = "0.8", features = ["serde"] }
zeroize = "1.7"

# Configuration
config = "0.13"
clap = { version = "4.4", features = ["derive"] }

# Metrics
prometheus = { version = "0.13", features = ["process"] }
metrics = "0.22"
metrics-prometheus = "0.6"

[dev-dependencies]
tempfile = "3.8"
mockall = "0.12"
wiremock = "0.5"
proptest = "1.4"
criterion = "0.5"

Database Schema

-- migrations/001_initial_schema.sql

-- Users table with security enhancements
CREATE TABLE users (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    username VARCHAR(255) UNIQUE NOT NULL,
    email VARCHAR(255) UNIQUE NOT NULL,
    password_hash VARCHAR(255) NOT NULL,
    salt VARCHAR(255) NOT NULL,
    is_active BOOLEAN NOT NULL DEFAULT true,
    is_verified BOOLEAN NOT NULL DEFAULT false,
    failed_login_attempts INTEGER NOT NULL DEFAULT 0,
    locked_until TIMESTAMP WITH TIME ZONE,
    last_login TIMESTAMP WITH TIME ZONE,
    password_changed_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),

    -- Security constraints
    CONSTRAINT users_username_length CHECK (char_length(username) >= 3),
    CONSTRAINT users_email_format CHECK (email ~* '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$')
);

-- User profiles for additional information
CREATE TABLE user_profiles (
    user_id UUID PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE,
    first_name VARCHAR(255),
    last_name VARCHAR(255),
    phone_number VARCHAR(20),
    timezone VARCHAR(50) DEFAULT 'UTC',
    locale VARCHAR(10) DEFAULT 'en-US',
    avatar_url TEXT,
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);

-- Roles and permissions (RBAC)
CREATE TABLE roles (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    name VARCHAR(100) UNIQUE NOT NULL,
    description TEXT,
    is_system_role BOOLEAN NOT NULL DEFAULT false,
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);

CREATE TABLE permissions (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    name VARCHAR(100) UNIQUE NOT NULL,
    resource VARCHAR(100) NOT NULL,
    action VARCHAR(50) NOT NULL,
    description TEXT,
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),

    UNIQUE(resource, action)
);

CREATE TABLE role_permissions (
    role_id UUID REFERENCES roles(id) ON DELETE CASCADE,
    permission_id UUID REFERENCES permissions(id) ON DELETE CASCADE,
    granted_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    granted_by UUID REFERENCES users(id),

    PRIMARY KEY (role_id, permission_id)
);

CREATE TABLE user_roles (
    user_id UUID REFERENCES users(id) ON DELETE CASCADE,
    role_id UUID REFERENCES roles(id) ON DELETE CASCADE,
    assigned_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    assigned_by UUID REFERENCES users(id),
    expires_at TIMESTAMP WITH TIME ZONE,

    PRIMARY KEY (user_id, role_id)
);

-- MFA configuration
CREATE TABLE user_mfa (
    user_id UUID PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE,
    is_enabled BOOLEAN NOT NULL DEFAULT false,
    secret_key VARCHAR(255), -- Encrypted TOTP secret
    backup_codes TEXT[], -- Encrypted backup codes
    recovery_codes_used INTEGER NOT NULL DEFAULT 0,
    last_used_at TIMESTAMP WITH TIME ZONE,
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);

-- OAuth2 clients and tokens
CREATE TABLE oauth2_clients (
    client_id VARCHAR(255) PRIMARY KEY,
    client_secret_hash VARCHAR(255) NOT NULL,
    client_name VARCHAR(255) NOT NULL,
    redirect_uris TEXT[] NOT NULL,
    allowed_scopes TEXT[] NOT NULL,
    is_confidential BOOLEAN NOT NULL DEFAULT true,
    token_lifetime_seconds INTEGER NOT NULL DEFAULT 3600,
    refresh_token_lifetime_seconds INTEGER NOT NULL DEFAULT 86400,
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);

CREATE TABLE oauth2_authorization_codes (
    code VARCHAR(255) PRIMARY KEY,
    client_id VARCHAR(255) NOT NULL REFERENCES oauth2_clients(client_id),
    user_id UUID NOT NULL REFERENCES users(id),
    redirect_uri TEXT NOT NULL,
    scope TEXT NOT NULL,
    code_challenge VARCHAR(255),
    code_challenge_method VARCHAR(10),
    expires_at TIMESTAMP WITH TIME ZONE NOT NULL,
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);

CREATE TABLE oauth2_access_tokens (
    token_hash VARCHAR(255) PRIMARY KEY,
    client_id VARCHAR(255) NOT NULL REFERENCES oauth2_clients(client_id),
    user_id UUID NOT NULL REFERENCES users(id),
    scope TEXT NOT NULL,
    expires_at TIMESTAMP WITH TIME ZONE NOT NULL,
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);

CREATE TABLE oauth2_refresh_tokens (
    token_hash VARCHAR(255) PRIMARY KEY,
    access_token_hash VARCHAR(255) NOT NULL,
    client_id VARCHAR(255) NOT NULL REFERENCES oauth2_clients(client_id),
    user_id UUID NOT NULL REFERENCES users(id),
    scope TEXT NOT NULL,
    expires_at TIMESTAMP WITH TIME ZONE NOT NULL,
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);

-- Session management
CREATE TABLE user_sessions (
    session_id VARCHAR(255) PRIMARY KEY,
    user_id UUID NOT NULL REFERENCES users(id),
    client_info JSONB NOT NULL,
    ip_address INET,
    user_agent TEXT,
    expires_at TIMESTAMP WITH TIME ZONE NOT NULL,
    last_activity TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    is_revoked BOOLEAN NOT NULL DEFAULT false,
    revoked_at TIMESTAMP WITH TIME ZONE,
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);

-- Audit logging
CREATE TABLE audit_logs (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id UUID REFERENCES users(id),
    action VARCHAR(100) NOT NULL,
    resource VARCHAR(100),
    resource_id VARCHAR(255),
    ip_address INET,
    user_agent TEXT,
    request_id VARCHAR(255),
    details JSONB,
    severity VARCHAR(20) NOT NULL DEFAULT 'INFO',
    timestamp TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);

-- Indexes for performance
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_username ON users(username);
CREATE INDEX idx_users_last_login ON users(last_login);
CREATE INDEX idx_user_sessions_user_id ON user_sessions(user_id);
CREATE INDEX idx_user_sessions_expires_at ON user_sessions(expires_at);
CREATE INDEX idx_oauth2_access_tokens_expires_at ON oauth2_access_tokens(expires_at);
CREATE INDEX idx_oauth2_refresh_tokens_expires_at ON oauth2_refresh_tokens(expires_at);
CREATE INDEX idx_audit_logs_user_id ON audit_logs(user_id);
CREATE INDEX idx_audit_logs_timestamp ON audit_logs(timestamp);

-- Initial system roles and permissions
INSERT INTO roles (name, description, is_system_role) VALUES
    ('super_admin', 'System administrator with full access', true),
    ('admin', 'Administrator with most permissions', true),
    ('user', 'Standard user with basic permissions', true),
    ('readonly', 'Read-only access to resources', true);

INSERT INTO permissions (name, resource, action, description) VALUES
    ('users.create', 'users', 'create', 'Create new users'),
    ('users.read', 'users', 'read', 'View user information'),
    ('users.update', 'users', 'update', 'Update user information'),
    ('users.delete', 'users', 'delete', 'Delete users'),
    ('roles.manage', 'roles', 'manage', 'Manage roles and permissions'),
    ('system.admin', 'system', 'admin', 'System administration access'),
    ('audit.read', 'audit', 'read', 'View audit logs');

JWT Token Management

Secure JWT Implementation

// src/auth/jwt.rs
use jsonwebtoken::{decode, encode, Algorithm, DecodingKey, EncodingKey, Header, Validation};
use serde::{Deserialize, Serialize};
use std::collections::HashSet;
use chrono::{DateTime, Duration, Utc};
use uuid::Uuid;
use anyhow::Result;

#[derive(Debug, Clone)]
pub struct JwtManager {
    encoding_key: EncodingKey,
    decoding_key: DecodingKey,
    algorithm: Algorithm,
    access_token_lifetime: Duration,
    refresh_token_lifetime: Duration,
    issuer: String,
    audience: HashSet<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Claims {
    // Standard JWT claims
    pub sub: String,          // Subject (user ID)
    pub iss: String,          // Issuer
    pub aud: Vec<String>,     // Audience
    pub exp: i64,             // Expiration time
    pub iat: i64,             // Issued at
    pub nbf: i64,             // Not before
    pub jti: String,          // JWT ID

    // Custom claims
    pub username: String,
    pub email: String,
    pub roles: Vec<String>,
    pub permissions: Vec<String>,
    pub scope: String,
    pub token_type: TokenType,
    pub session_id: String,
    pub client_id: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum TokenType {
    Access,
    Refresh,
    IdToken,
}

#[derive(Debug, Clone)]
pub struct TokenPair {
    pub access_token: String,
    pub refresh_token: String,
    pub expires_in: u64,
    pub token_type: String,
}

impl JwtManager {
    pub fn new(
        secret: &[u8],
        issuer: String,
        audience: HashSet<String>,
        access_token_lifetime: Duration,
        refresh_token_lifetime: Duration,
    ) -> Result<Self> {
        // Use RS256 for production (requires RSA key pair)
        // For this example, we'll use HS256 with a secure secret
        let encoding_key = EncodingKey::from_secret(secret);
        let decoding_key = DecodingKey::from_secret(secret);

        Ok(Self {
            encoding_key,
            decoding_key,
            algorithm: Algorithm::HS256,
            access_token_lifetime,
            refresh_token_lifetime,
            issuer,
            audience,
        })
    }

    pub fn create_token_pair(
        &self,
        user_id: Uuid,
        username: String,
        email: String,
        roles: Vec<String>,
        permissions: Vec<String>,
        scope: String,
        session_id: String,
        client_id: Option<String>,
    ) -> Result<TokenPair> {
        let now = Utc::now();

        // Create access token
        let access_claims = Claims {
            sub: user_id.to_string(),
            iss: self.issuer.clone(),
            aud: self.audience.iter().cloned().collect(),
            exp: (now + self.access_token_lifetime).timestamp(),
            iat: now.timestamp(),
            nbf: now.timestamp(),
            jti: Uuid::new_v4().to_string(),
            username: username.clone(),
            email: email.clone(),
            roles: roles.clone(),
            permissions,
            scope: scope.clone(),
            token_type: TokenType::Access,
            session_id: session_id.clone(),
            client_id: client_id.clone(),
        };

        let access_token = encode(
            &Header::new(self.algorithm),
            &access_claims,
            &self.encoding_key,
        )?;

        // Create refresh token
        let refresh_claims = Claims {
            sub: user_id.to_string(),
            iss: self.issuer.clone(),
            aud: self.audience.iter().cloned().collect(),
            exp: (now + self.refresh_token_lifetime).timestamp(),
            iat: now.timestamp(),
            nbf: now.timestamp(),
            jti: Uuid::new_v4().to_string(),
            username,
            email,
            roles,
            permissions: vec![], // Refresh tokens don't need permissions
            scope,
            token_type: TokenType::Refresh,
            session_id,
            client_id,
        };

        let refresh_token = encode(
            &Header::new(self.algorithm),
            &refresh_claims,
            &self.encoding_key,
        )?;

        Ok(TokenPair {
            access_token,
            refresh_token,
            expires_in: self.access_token_lifetime.num_seconds() as u64,
            token_type: "Bearer".to_string(),
        })
    }

    pub fn verify_token(&self, token: &str) -> Result<Claims> {
        let mut validation = Validation::new(self.algorithm);
        validation.set_issuer(&[&self.issuer]);
        validation.set_audience(&self.audience);
        validation.validate_exp = true;
        validation.validate_nbf = true;

        let token_data = decode::<Claims>(token, &self.decoding_key, &validation)?;
        Ok(token_data.claims)
    }

    pub fn refresh_access_token(
        &self,
        refresh_token: &str,
        user_service: &UserService,
    ) -> Result<TokenPair> {
        // Verify refresh token
        let claims = self.verify_token(refresh_token)?;

        // Ensure it's a refresh token
        if claims.token_type != TokenType::Refresh {
            return Err(anyhow::anyhow!("Invalid token type for refresh"));
        }

        // Get updated user information
        let user_id = Uuid::parse_str(&claims.sub)?;
        let user = user_service.get_user_by_id(user_id)?
            .ok_or_else(|| anyhow::anyhow!("User not found"))?;

        // Check if user is still active
        if !user.is_active {
            return Err(anyhow::anyhow!("User account is inactive"));
        }

        // Get current roles and permissions
        let roles = user_service.get_user_roles(user_id)?;
        let permissions = user_service.get_user_permissions(user_id)?;

        // Create new token pair
        self.create_token_pair(
            user_id,
            user.username,
            user.email,
            roles,
            permissions,
            claims.scope,
            claims.session_id,
            claims.client_id,
        )
    }

    pub fn extract_bearer_token(authorization_header: &str) -> Option<&str> {
        authorization_header
            .strip_prefix("Bearer ")
            .map(|token| token.trim())
    }

    pub fn validate_token_permissions(&self, token: &str, required_permission: &str) -> Result<bool> {
        let claims = self.verify_token(token)?;
        Ok(claims.permissions.contains(&required_permission.to_string()))
    }

    pub fn validate_token_roles(&self, token: &str, required_roles: &[String]) -> Result<bool> {
        let claims = self.verify_token(token)?;
        Ok(required_roles.iter().any(|role| claims.roles.contains(role)))
    }

    pub fn get_token_info(&self, token: &str) -> Result<TokenInfo> {
        let claims = self.verify_token(token)?;

        Ok(TokenInfo {
            user_id: Uuid::parse_str(&claims.sub)?,
            username: claims.username,
            email: claims.email,
            roles: claims.roles,
            permissions: claims.permissions,
            expires_at: DateTime::from_timestamp(claims.exp, 0)
                .ok_or_else(|| anyhow::anyhow!("Invalid expiration timestamp"))?,
            session_id: claims.session_id,
            client_id: claims.client_id,
        })
    }
}

#[derive(Debug, Clone)]
pub struct TokenInfo {
    pub user_id: Uuid,
    pub username: String,
    pub email: String,
    pub roles: Vec<String>,
    pub permissions: Vec<String>,
    pub expires_at: DateTime<Utc>,
    pub session_id: String,
    pub client_id: Option<String>,
}

// Secure token storage for refresh tokens
#[derive(Debug, Clone)]
pub struct TokenStore {
    redis: redis::Client,
}

impl TokenStore {
    pub fn new(redis_url: &str) -> Result<Self> {
        let redis = redis::Client::open(redis_url)?;
        Ok(Self { redis })
    }

    pub async fn store_refresh_token(
        &self,
        user_id: Uuid,
        token_hash: &str,
        expires_at: DateTime<Utc>,
    ) -> Result<()> {
        let mut conn = self.redis.get_multiplexed_async_connection().await?;
        let key = format!("refresh_token:{}", user_id);
        let ttl = (expires_at - Utc::now()).num_seconds();

        redis::cmd("SETEX")
            .arg(&key)
            .arg(ttl)
            .arg(token_hash)
            .exec_async(&mut conn)
            .await?;

        Ok(())
    }

    pub async fn validate_refresh_token(
        &self,
        user_id: Uuid,
        token_hash: &str,
    ) -> Result<bool> {
        let mut conn = self.redis.get_multiplexed_async_connection().await?;
        let key = format!("refresh_token:{}", user_id);

        let stored_hash: Option<String> = redis::cmd("GET")
            .arg(&key)
            .query_async(&mut conn)
            .await?;

        Ok(stored_hash.as_deref() == Some(token_hash))
    }

    pub async fn revoke_refresh_token(&self, user_id: Uuid) -> Result<()> {
        let mut conn = self.redis.get_multiplexed_async_connection().await?;
        let key = format!("refresh_token:{}", user_id);

        redis::cmd("DEL")
            .arg(&key)
            .exec_async(&mut conn)
            .await?;

        Ok(())
    }

    pub async fn revoke_all_user_tokens(&self, user_id: Uuid) -> Result<()> {
        let mut conn = self.redis.get_multiplexed_async_connection().await?;
        let pattern = format!("*:{}:*", user_id);

        let keys: Vec<String> = redis::cmd("KEYS")
            .arg(&pattern)
            .query_async(&mut conn)
            .await?;

        if !keys.is_empty() {
            redis::cmd("DEL")
                .arg(&keys)
                .exec_async(&mut conn)
                .await?;
        }

        Ok(())
    }
}

// JWT Middleware for Axum
#[derive(Clone)]
pub struct JwtAuthLayer {
    jwt_manager: Arc<JwtManager>,
}

impl JwtAuthLayer {
    pub fn new(jwt_manager: Arc<JwtManager>) -> Self {
        Self { jwt_manager }
    }
}

use axum::{
    extract::Request,
    http::{HeaderMap, StatusCode},
    middleware::Next,
    response::Response,
};

pub async fn jwt_auth_middleware(
    headers: HeaderMap,
    mut request: Request,
    next: Next,
) -> Result<Response, StatusCode> {
    let auth_header = headers
        .get("authorization")
        .and_then(|h| h.to_str().ok())
        .ok_or(StatusCode::UNAUTHORIZED)?;

    let token = JwtManager::extract_bearer_token(auth_header)
        .ok_or(StatusCode::UNAUTHORIZED)?;

    // Get JWT manager from app state
    let jwt_manager = request
        .extensions()
        .get::<Arc<JwtManager>>()
        .ok_or(StatusCode::INTERNAL_SERVER_ERROR)?;

    let claims = jwt_manager
        .verify_token(token)
        .map_err(|_| StatusCode::UNAUTHORIZED)?;

    // Add claims to request extensions
    request.extensions_mut().insert(claims);

    Ok(next.run(request).await)
}

// Permission-based authorization middleware
pub fn require_permission(permission: &'static str) -> impl Fn(Request, Next) -> impl std::future::Future<Output = Result<Response, StatusCode>> + Clone {
    move |request: Request, next: Next| async move {
        let claims = request
            .extensions()
            .get::<Claims>()
            .ok_or(StatusCode::UNAUTHORIZED)?;

        if !claims.permissions.contains(&permission.to_string()) {
            return Err(StatusCode::FORBIDDEN);
        }

        Ok(next.run(request).await)
    }
}

// Role-based authorization middleware
pub fn require_role(roles: &'static [&'static str]) -> impl Fn(Request, Next) -> impl std::future::Future<Output = Result<Response, StatusCode>> + Clone {
    move |request: Request, next: Next| async move {
        let claims = request
            .extensions()
            .get::<Claims>()
            .ok_or(StatusCode::UNAUTHORIZED)?;

        let has_role = roles.iter().any(|role| claims.roles.contains(&role.to_string()));
        if !has_role {
            return Err(StatusCode::FORBIDDEN);
        }

        Ok(next.run(request).await)
    }
}

Multi-Factor Authentication (MFA)

TOTP Implementation

// src/auth/mfa.rs
use totp_rs::{Algorithm, Secret, TOTP};
use qrcode::{QrCode, render::svg};
use ring::rand::{SecureRandom, SystemRandom};
use aes_gcm::{Aes256Gcm, Key, Nonce, aead::{Aead, NewAead}};
use base64::{Engine as _, engine::general_purpose};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use chrono::{DateTime, Utc};
use anyhow::Result;

#[derive(Debug, Clone)]
pub struct MfaManager {
    encryption_key: [u8; 32],
    issuer: String,
    backup_codes_count: usize,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MfaSetup {
    pub secret_key: String,
    pub qr_code_svg: String,
    pub backup_codes: Vec<String>,
    pub setup_uri: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MfaConfig {
    pub user_id: Uuid,
    pub is_enabled: bool,
    pub secret_key_encrypted: String,
    pub backup_codes_encrypted: Vec<String>,
    pub recovery_codes_used: i32,
    pub last_used_at: Option<DateTime<Utc>>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MfaVerificationRequest {
    pub user_id: Uuid,
    pub token: String,
    pub backup_code: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MfaVerificationResponse {
    pub is_valid: bool,
    pub backup_code_used: bool,
    pub remaining_backup_codes: usize,
}

impl MfaManager {
    pub fn new(encryption_key: [u8; 32], issuer: String) -> Self {
        Self {
            encryption_key,
            issuer,
            backup_codes_count: 10,
        }
    }

    pub fn generate_mfa_setup(&self, user_id: Uuid, username: &str, email: &str) -> Result<MfaSetup> {
        // Generate a random secret key
        let secret = Secret::generate_secret();
        let secret_str = secret.to_encoded().to_string();

        // Create TOTP instance
        let totp = TOTP::new(
            Algorithm::SHA1,
            6, // 6-digit codes
            1, // 30-second time step
            30,
            secret.to_bytes().unwrap(),
            Some(self.issuer.clone()),
            username.to_string(),
        )?;

        // Generate QR code
        let setup_uri = totp.get_url();
        let qr_code = QrCode::new(&setup_uri)?;
        let qr_code_svg = qr_code.render::<svg::Color>()
            .min_dimensions(200, 200)
            .dark_color(svg::Color("#000000"))
            .light_color(svg::Color("#ffffff"))
            .build();

        // Generate backup codes
        let backup_codes = self.generate_backup_codes()?;

        Ok(MfaSetup {
            secret_key: secret_str,
            qr_code_svg,
            backup_codes,
            setup_uri,
        })
    }

    pub fn enable_mfa(&self, user_id: Uuid, secret_key: &str, backup_codes: &[String]) -> Result<MfaConfig> {
        // Encrypt secret key and backup codes
        let secret_encrypted = self.encrypt_data(secret_key.as_bytes())?;
        let backup_codes_encrypted: Result<Vec<String>> = backup_codes
            .iter()
            .map(|code| self.encrypt_data(code.as_bytes()))
            .collect();

        Ok(MfaConfig {
            user_id,
            is_enabled: true,
            secret_key_encrypted: secret_encrypted,
            backup_codes_encrypted: backup_codes_encrypted?,
            recovery_codes_used: 0,
            last_used_at: None,
        })
    }

    pub fn verify_totp_token(&self, config: &MfaConfig, token: &str) -> Result<bool> {
        if !config.is_enabled {
            return Ok(false);
        }

        // Decrypt secret key
        let secret_key = self.decrypt_data(&config.secret_key_encrypted)?;
        let secret = Secret::Encoded(String::from_utf8(secret_key)?);

        // Create TOTP instance
        let totp = TOTP::new(
            Algorithm::SHA1,
            6,
            1,
            30,
            secret.to_bytes().unwrap(),
            Some(self.issuer.clone()),
            "user".to_string(), // Username not needed for verification
        )?;

        // Verify token with time tolerance (allows for clock skew)
        let current_time = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)?
            .as_secs();

        // Check current time step and adjacent time steps
        for time_step_offset in [-1, 0, 1] {
            let check_time = (current_time as i64 + (time_step_offset * 30)) as u64;
            if totp.generate(check_time) == token {
                return Ok(true);
            }
        }

        Ok(false)
    }

    pub fn verify_backup_code(&self, config: &mut MfaConfig, backup_code: &str) -> Result<bool> {
        if !config.is_enabled {
            return Ok(false);
        }

        // Check if backup code exists and hasn't been used
        for (index, encrypted_code) in config.backup_codes_encrypted.iter().enumerate() {
            let decrypted_code = self.decrypt_data(encrypted_code)?;
            let code_str = String::from_utf8(decrypted_code)?;

            if code_str == backup_code {
                // Mark code as used by removing it
                config.backup_codes_encrypted.remove(index);
                config.recovery_codes_used += 1;
                return Ok(true);
            }
        }

        Ok(false)
    }

    pub async fn verify_mfa(
        &self,
        mut config: MfaConfig,
        request: &MfaVerificationRequest,
    ) -> Result<MfaVerificationResponse> {
        let mut backup_code_used = false;
        let mut is_valid = false;

        // First try TOTP token if provided
        if !request.token.is_empty() {
            is_valid = self.verify_totp_token(&config, &request.token)?;
        }

        // If TOTP failed and backup code provided, try backup code
        if !is_valid && request.backup_code.is_some() {
            let backup_code = request.backup_code.as_ref().unwrap();
            is_valid = self.verify_backup_code(&mut config, backup_code)?;
            backup_code_used = is_valid;
        }

        // Update last used time if verification succeeded
        if is_valid {
            // This would typically be saved to database
            // config.last_used_at = Some(Utc::now());
        }

        Ok(MfaVerificationResponse {
            is_valid,
            backup_code_used,
            remaining_backup_codes: config.backup_codes_encrypted.len(),
        })
    }

    pub fn generate_backup_codes(&self) -> Result<Vec<String>> {
        let rng = SystemRandom::new();
        let mut codes = Vec::with_capacity(self.backup_codes_count);

        for _ in 0..self.backup_codes_count {
            let mut code_bytes = [0u8; 5];
            rng.fill(&mut code_bytes)?;

            // Convert to readable format (XXXXX-XXXXX)
            let code = format!(
                "{:05}-{:05}",
                u32::from_be_bytes([0, code_bytes[0], code_bytes[1], code_bytes[2]]) % 100000,
                u32::from_be_bytes([0, code_bytes[2], code_bytes[3], code_bytes[4]]) % 100000
            );
            codes.push(code);
        }

        Ok(codes)
    }

    fn encrypt_data(&self, data: &[u8]) -> Result<String> {
        let cipher = Aes256Gcm::new(Key::from_slice(&self.encryption_key));

        // Generate random nonce
        let rng = SystemRandom::new();
        let mut nonce_bytes = [0u8; 12];
        rng.fill(&mut nonce_bytes)?;
        let nonce = Nonce::from_slice(&nonce_bytes);

        // Encrypt data
        let ciphertext = cipher.encrypt(nonce, data)
            .map_err(|e| anyhow::anyhow!("Encryption failed: {}", e))?;

        // Combine nonce and ciphertext
        let mut result = nonce_bytes.to_vec();
        result.extend_from_slice(&ciphertext);

        Ok(general_purpose::STANDARD.encode(&result))
    }

    fn decrypt_data(&self, encrypted_data: &str) -> Result<Vec<u8>> {
        let cipher = Aes256Gcm::new(Key::from_slice(&self.encryption_key));

        // Decode base64
        let data = general_purpose::STANDARD.decode(encrypted_data)?;

        if data.len() < 12 {
            return Err(anyhow::anyhow!("Invalid encrypted data length"));
        }

        // Extract nonce and ciphertext
        let (nonce_bytes, ciphertext) = data.split_at(12);
        let nonce = Nonce::from_slice(nonce_bytes);

        // Decrypt data
        let plaintext = cipher.decrypt(nonce, ciphertext)
            .map_err(|e| anyhow::anyhow!("Decryption failed: {}", e))?;

        Ok(plaintext)
    }

    pub fn disable_mfa(&self, config: &mut MfaConfig) -> Result<()> {
        config.is_enabled = false;
        config.secret_key_encrypted.clear();
        config.backup_codes_encrypted.clear();
        config.recovery_codes_used = 0;
        config.last_used_at = None;
        Ok(())
    }

    pub fn regenerate_backup_codes(&self, config: &mut MfaConfig) -> Result<Vec<String>> {
        let new_codes = self.generate_backup_codes()?;

        // Encrypt new codes
        let encrypted_codes: Result<Vec<String>> = new_codes
            .iter()
            .map(|code| self.encrypt_data(code.as_bytes()))
            .collect();

        config.backup_codes_encrypted = encrypted_codes?;
        config.recovery_codes_used = 0;

        Ok(new_codes)
    }

    pub fn get_mfa_status(&self, config: &MfaConfig) -> MfaStatus {
        MfaStatus {
            is_enabled: config.is_enabled,
            backup_codes_remaining: config.backup_codes_encrypted.len(),
            last_used_at: config.last_used_at,
            setup_required: !config.is_enabled && config.secret_key_encrypted.is_empty(),
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MfaStatus {
    pub is_enabled: bool,
    pub backup_codes_remaining: usize,
    pub last_used_at: Option<DateTime<Utc>>,
    pub setup_required: bool,
}

// Rate limiting for MFA attempts
#[derive(Debug, Clone)]
pub struct MfaRateLimiter {
    redis: redis::Client,
    max_attempts: u32,
    window_seconds: u64,
    lockout_duration_seconds: u64,
}

impl MfaRateLimiter {
    pub fn new(redis_url: &str) -> Result<Self> {
        Ok(Self {
            redis: redis::Client::open(redis_url)?,
            max_attempts: 5,
            window_seconds: 300, // 5 minutes
            lockout_duration_seconds: 900, // 15 minutes
        })
    }

    pub async fn check_rate_limit(&self, user_id: Uuid) -> Result<bool> {
        let mut conn = self.redis.get_multiplexed_async_connection().await?;
        let key = format!("mfa_attempts:{}", user_id);

        // Check if user is locked out
        let lockout_key = format!("mfa_lockout:{}", user_id);
        let is_locked: Option<String> = redis::cmd("GET")
            .arg(&lockout_key)
            .query_async(&mut conn)
            .await?;

        if is_locked.is_some() {
            return Ok(false); // Rate limited
        }

        // Check attempt count
        let attempts: Option<u32> = redis::cmd("GET")
            .arg(&key)
            .query_async(&mut conn)
            .await?;

        let current_attempts = attempts.unwrap_or(0);

        if current_attempts >= self.max_attempts {
            // Lock out user
            redis::cmd("SETEX")
                .arg(&lockout_key)
                .arg(self.lockout_duration_seconds)
                .arg("locked")
                .exec_async(&mut conn)
                .await?;

            // Clear attempts counter
            redis::cmd("DEL")
                .arg(&key)
                .exec_async(&mut conn)
                .await?;

            return Ok(false);
        }

        Ok(true)
    }

    pub async fn record_attempt(&self, user_id: Uuid, success: bool) -> Result<()> {
        let mut conn = self.redis.get_multiplexed_async_connection().await?;
        let key = format!("mfa_attempts:{}", user_id);

        if success {
            // Clear attempts on success
            redis::cmd("DEL")
                .arg(&key)
                .exec_async(&mut conn)
                .await?;
        } else {
            // Increment attempts
            redis::cmd("INCR")
                .arg(&key)
                .exec_async(&mut conn)
                .await?;

            // Set expiration on first attempt
            redis::cmd("EXPIRE")
                .arg(&key)
                .arg(self.window_seconds)
                .exec_async(&mut conn)
                .await?;
        }

        Ok(())
    }
}

OAuth2 Implementation

OAuth2 Authorization Server

// src/auth/oauth2.rs
use oauth2::{
    AuthUrl, AuthorizationCode, ClientId, ClientSecret, CsrfToken, PkceCodeChallenge,
    PkceCodeVerifier, RedirectUrl, Scope, TokenUrl,
};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use chrono::{DateTime, Duration, Utc};
use url::Url;
use anyhow::Result;
use std::collections::HashMap;

#[derive(Debug, Clone)]
pub struct OAuth2Provider {
    clients: HashMap<String, OAuth2Client>,
    auth_codes: Arc<Mutex<HashMap<String, AuthorizationCodeGrant>>>,
    access_tokens: Arc<Mutex<HashMap<String, AccessTokenGrant>>>,
    refresh_tokens: Arc<Mutex<HashMap<String, RefreshTokenGrant>>>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OAuth2Client {
    pub client_id: String,
    pub client_secret_hash: String,
    pub client_name: String,
    pub redirect_uris: Vec<String>,
    pub allowed_scopes: Vec<String>,
    pub is_confidential: bool,
    pub token_lifetime_seconds: u64,
    pub refresh_token_lifetime_seconds: u64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuthorizationRequest {
    pub response_type: String,
    pub client_id: String,
    pub redirect_uri: String,
    pub scope: Option<String>,
    pub state: Option<String>,
    pub code_challenge: Option<String>,
    pub code_challenge_method: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuthorizationResponse {
    pub code: String,
    pub state: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TokenRequest {
    pub grant_type: String,
    pub code: Option<String>,
    pub redirect_uri: Option<String>,
    pub client_id: String,
    pub client_secret: Option<String>,
    pub code_verifier: Option<String>,
    pub refresh_token: Option<String>,
    pub scope: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TokenResponse {
    pub access_token: String,
    pub token_type: String,
    pub expires_in: u64,
    pub refresh_token: Option<String>,
    pub scope: String,
    pub id_token: Option<String>,
}

#[derive(Debug, Clone)]
struct AuthorizationCodeGrant {
    client_id: String,
    user_id: Uuid,
    redirect_uri: String,
    scope: String,
    code_challenge: Option<String>,
    code_challenge_method: Option<String>,
    expires_at: DateTime<Utc>,
}

#[derive(Debug, Clone)]
struct AccessTokenGrant {
    client_id: String,
    user_id: Uuid,
    scope: String,
    expires_at: DateTime<Utc>,
}

#[derive(Debug, Clone)]
struct RefreshTokenGrant {
    access_token_hash: String,
    client_id: String,
    user_id: Uuid,
    scope: String,
    expires_at: DateTime<Utc>,
}

impl OAuth2Provider {
    pub fn new() -> Self {
        Self {
            clients: HashMap::new(),
            auth_codes: Arc::new(Mutex::new(HashMap::new())),
            access_tokens: Arc::new(Mutex::new(HashMap::new())),
            refresh_tokens: Arc::new(Mutex::new(HashMap::new())),
        }
    }

    pub fn register_client(&mut self, client: OAuth2Client) {
        self.clients.insert(client.client_id.clone(), client);
    }

    pub fn authorize(
        &self,
        request: &AuthorizationRequest,
        user_id: Uuid,
    ) -> Result<AuthorizationResponse> {
        // Validate client
        let client = self.clients.get(&request.client_id)
            .ok_or_else(|| anyhow::anyhow!("Invalid client_id"))?;

        // Validate redirect URI
        if !client.redirect_uris.contains(&request.redirect_uri) {
            return Err(anyhow::anyhow!("Invalid redirect_uri"));
        }

        // Validate response type
        if request.response_type != "code" {
            return Err(anyhow::anyhow!("Unsupported response_type"));
        }

        // Validate and parse scopes
        let requested_scopes = request.scope
            .as_ref()
            .map(|s| s.split_whitespace().collect::<Vec<_>>())
            .unwrap_or_default();

        // Check if all requested scopes are allowed
        for scope in &requested_scopes {
            if !client.allowed_scopes.contains(&scope.to_string()) {
                return Err(anyhow::anyhow!("Invalid scope: {}", scope));
            }
        }

        // Generate authorization code
        let auth_code = self.generate_authorization_code();
        let expires_at = Utc::now() + Duration::minutes(10); // 10-minute expiry

        // Store authorization grant
        let grant = AuthorizationCodeGrant {
            client_id: request.client_id.clone(),
            user_id,
            redirect_uri: request.redirect_uri.clone(),
            scope: requested_scopes.join(" "),
            code_challenge: request.code_challenge.clone(),
            code_challenge_method: request.code_challenge_method.clone(),
            expires_at,
        };

        self.auth_codes.lock().unwrap().insert(auth_code.clone(), grant);

        Ok(AuthorizationResponse {
            code: auth_code,
            state: request.state.clone(),
        })
    }

    pub fn exchange_code_for_token(
        &self,
        request: &TokenRequest,
        jwt_manager: &JwtManager,
        user_service: &UserService,
    ) -> Result<TokenResponse> {
        match request.grant_type.as_str() {
            "authorization_code" => self.handle_authorization_code_grant(request, jwt_manager, user_service),
            "refresh_token" => self.handle_refresh_token_grant(request, jwt_manager, user_service),
            _ => Err(anyhow::anyhow!("Unsupported grant_type")),
        }
    }

    fn handle_authorization_code_grant(
        &self,
        request: &TokenRequest,
        jwt_manager: &JwtManager,
        user_service: &UserService,
    ) -> Result<TokenResponse> {
        let code = request.code.as_ref()
            .ok_or_else(|| anyhow::anyhow!("Missing authorization code"))?;

        // Get and remove authorization code grant
        let grant = {
            let mut codes = self.auth_codes.lock().unwrap();
            codes.remove(code)
                .ok_or_else(|| anyhow::anyhow!("Invalid or expired authorization code"))?
        };

        // Validate client
        let client = self.clients.get(&request.client_id)
            .ok_or_else(|| anyhow::anyhow!("Invalid client_id"))?;

        if grant.client_id != request.client_id {
            return Err(anyhow::anyhow!("Client ID mismatch"));
        }

        // Check expiration
        if Utc::now() > grant.expires_at {
            return Err(anyhow::anyhow!("Authorization code expired"));
        }

        // Validate redirect URI
        if let Some(redirect_uri) = &request.redirect_uri {
            if *redirect_uri != grant.redirect_uri {
                return Err(anyhow::anyhow!("Redirect URI mismatch"));
            }
        }

        // Validate PKCE if present
        if let Some(code_challenge) = &grant.code_challenge {
            let code_verifier = request.code_verifier.as_ref()
                .ok_or_else(|| anyhow::anyhow!("Missing code_verifier for PKCE"))?;

            if !self.verify_pkce_challenge(code_challenge, code_verifier, &grant.code_challenge_method)? {
                return Err(anyhow::anyhow!("Invalid PKCE code_verifier"));
            }
        }

        // Authenticate confidential clients
        if client.is_confidential {
            let client_secret = request.client_secret.as_ref()
                .ok_or_else(|| anyhow::anyhow!("Missing client_secret"))?;

            if !self.verify_client_secret(client, client_secret)? {
                return Err(anyhow::anyhow!("Invalid client_secret"));
            }
        }

        // Get user information
        let user = user_service.get_user_by_id(grant.user_id)?
            .ok_or_else(|| anyhow::anyhow!("User not found"))?;

        if !user.is_active {
            return Err(anyhow::anyhow!("User account is inactive"));
        }

        // Get user roles and permissions
        let roles = user_service.get_user_roles(grant.user_id)?;
        let permissions = user_service.get_user_permissions(grant.user_id)?;

        // Generate session ID
        let session_id = Uuid::new_v4().to_string();

        // Create JWT tokens
        let token_pair = jwt_manager.create_token_pair(
            grant.user_id,
            user.username.clone(),
            user.email.clone(),
            roles,
            permissions,
            grant.scope.clone(),
            session_id,
            Some(client.client_id.clone()),
        )?;

        // Store access token grant
        let access_token_hash = self.hash_token(&token_pair.access_token)?;
        let access_grant = AccessTokenGrant {
            client_id: client.client_id.clone(),
            user_id: grant.user_id,
            scope: grant.scope.clone(),
            expires_at: Utc::now() + Duration::seconds(client.token_lifetime_seconds as i64),
        };

        self.access_tokens.lock().unwrap().insert(access_token_hash.clone(), access_grant);

        // Store refresh token grant if refresh token is provided
        let refresh_token = if !token_pair.refresh_token.is_empty() {
            let refresh_token_hash = self.hash_token(&token_pair.refresh_token)?;
            let refresh_grant = RefreshTokenGrant {
                access_token_hash: access_token_hash.clone(),
                client_id: client.client_id.clone(),
                user_id: grant.user_id,
                scope: grant.scope.clone(),
                expires_at: Utc::now() + Duration::seconds(client.refresh_token_lifetime_seconds as i64),
            };

            self.refresh_tokens.lock().unwrap().insert(refresh_token_hash, refresh_grant);
            Some(token_pair.refresh_token)
        } else {
            None
        };

        Ok(TokenResponse {
            access_token: token_pair.access_token,
            token_type: token_pair.token_type,
            expires_in: token_pair.expires_in,
            refresh_token,
            scope: grant.scope,
            id_token: None, // Would implement OpenID Connect ID token here
        })
    }

    fn handle_refresh_token_grant(
        &self,
        request: &TokenRequest,
        jwt_manager: &JwtManager,
        user_service: &UserService,
    ) -> Result<TokenResponse> {
        let refresh_token = request.refresh_token.as_ref()
            .ok_or_else(|| anyhow::anyhow!("Missing refresh_token"))?;

        // Validate client
        let client = self.clients.get(&request.client_id)
            .ok_or_else(|| anyhow::anyhow!("Invalid client_id"))?;

        // Authenticate confidential clients
        if client.is_confidential {
            let client_secret = request.client_secret.as_ref()
                .ok_or_else(|| anyhow::anyhow!("Missing client_secret"))?;

            if !self.verify_client_secret(client, client_secret)? {
                return Err(anyhow::anyhow!("Invalid client_secret"));
            }
        }

        // Get and validate refresh token grant
        let refresh_token_hash = self.hash_token(refresh_token)?;
        let grant = {
            let refresh_tokens = self.refresh_tokens.lock().unwrap();
            refresh_tokens.get(&refresh_token_hash)
                .ok_or_else(|| anyhow::anyhow!("Invalid refresh_token"))?
                .clone()
        };

        if grant.client_id != request.client_id {
            return Err(anyhow::anyhow!("Client ID mismatch"));
        }

        // Check expiration
        if Utc::now() > grant.expires_at {
            return Err(anyhow::anyhow!("Refresh token expired"));
        }

        // Revoke old tokens
        self.revoke_access_token(&grant.access_token_hash)?;
        self.revoke_refresh_token(&refresh_token_hash)?;

        // Get user information
        let user = user_service.get_user_by_id(grant.user_id)?
            .ok_or_else(|| anyhow::anyhow!("User not found"))?;

        if !user.is_active {
            return Err(anyhow::anyhow!("User account is inactive"));
        }

        // Get current roles and permissions
        let roles = user_service.get_user_roles(grant.user_id)?;
        let permissions = user_service.get_user_permissions(grant.user_id)?;

        // Handle scope parameter
        let scope = if let Some(requested_scope) = &request.scope {
            // Validate that requested scope is a subset of original scope
            let original_scopes: Vec<&str> = grant.scope.split_whitespace().collect();
            let requested_scopes: Vec<&str> = requested_scope.split_whitespace().collect();

            for requested in &requested_scopes {
                if !original_scopes.contains(requested) {
                    return Err(anyhow::anyhow!("Invalid scope: {}", requested));
                }
            }

            requested_scope.clone()
        } else {
            grant.scope.clone()
        };

        // Generate new session ID
        let session_id = Uuid::new_v4().to_string();

        // Create new JWT tokens
        let token_pair = jwt_manager.create_token_pair(
            grant.user_id,
            user.username,
            user.email,
            roles,
            permissions,
            scope.clone(),
            session_id,
            Some(client.client_id.clone()),
        )?;

        // Store new access token grant
        let access_token_hash = self.hash_token(&token_pair.access_token)?;
        let access_grant = AccessTokenGrant {
            client_id: client.client_id.clone(),
            user_id: grant.user_id,
            scope: scope.clone(),
            expires_at: Utc::now() + Duration::seconds(client.token_lifetime_seconds as i64),
        };

        self.access_tokens.lock().unwrap().insert(access_token_hash.clone(), access_grant);

        // Store new refresh token grant
        let new_refresh_token_hash = self.hash_token(&token_pair.refresh_token)?;
        let new_refresh_grant = RefreshTokenGrant {
            access_token_hash: access_token_hash.clone(),
            client_id: client.client_id.clone(),
            user_id: grant.user_id,
            scope: scope.clone(),
            expires_at: Utc::now() + Duration::seconds(client.refresh_token_lifetime_seconds as i64),
        };

        self.refresh_tokens.lock().unwrap().insert(new_refresh_token_hash, new_refresh_grant);

        Ok(TokenResponse {
            access_token: token_pair.access_token,
            token_type: token_pair.token_type,
            expires_in: token_pair.expires_in,
            refresh_token: Some(token_pair.refresh_token),
            scope,
            id_token: None,
        })
    }

    fn generate_authorization_code(&self) -> String {
        use ring::rand::{SecureRandom, SystemRandom};
        let rng = SystemRandom::new();
        let mut code_bytes = [0u8; 32];
        rng.fill(&mut code_bytes).unwrap();
        base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(&code_bytes)
    }

    fn verify_pkce_challenge(
        &self,
        code_challenge: &str,
        code_verifier: &str,
        method: &Option<String>,
    ) -> Result<bool> {
        let method = method.as_deref().unwrap_or("plain");

        match method {
            "plain" => Ok(code_challenge == code_verifier),
            "S256" => {
                use sha2::{Sha256, Digest};
                let mut hasher = Sha256::new();
                hasher.update(code_verifier.as_bytes());
                let hash = hasher.finalize();
                let encoded = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(&hash);
                Ok(code_challenge == encoded)
            }
            _ => Err(anyhow::anyhow!("Unsupported code_challenge_method")),
        }
    }

    fn verify_client_secret(&self, client: &OAuth2Client, secret: &str) -> Result<bool> {
        use argon2::{Argon2, PasswordHash, PasswordVerifier};

        let parsed_hash = PasswordHash::new(&client.client_secret_hash)
            .map_err(|e| anyhow::anyhow!("Invalid client secret hash: {}", e))?;

        Ok(Argon2::default()
            .verify_password(secret.as_bytes(), &parsed_hash)
            .is_ok())
    }

    fn hash_token(&self, token: &str) -> Result<String> {
        use sha2::{Sha256, Digest};
        let mut hasher = Sha256::new();
        hasher.update(token.as_bytes());
        Ok(hex::encode(hasher.finalize()))
    }

    fn revoke_access_token(&self, token_hash: &str) -> Result<()> {
        self.access_tokens.lock().unwrap().remove(token_hash);
        Ok(())
    }

    fn revoke_refresh_token(&self, token_hash: &str) -> Result<()> {
        self.refresh_tokens.lock().unwrap().remove(token_hash);
        Ok(())
    }

    pub fn validate_access_token(&self, token: &str) -> Result<Option<AccessTokenInfo>> {
        let token_hash = self.hash_token(token)?;
        let grants = self.access_tokens.lock().unwrap();

        if let Some(grant) = grants.get(&token_hash) {
            if Utc::now() <= grant.expires_at {
                return Ok(Some(AccessTokenInfo {
                    client_id: grant.client_id.clone(),
                    user_id: grant.user_id,
                    scope: grant.scope.clone(),
                    expires_at: grant.expires_at,
                }));
            }
        }

        Ok(None)
    }

    pub fn introspect_token(&self, token: &str, client_id: &str) -> Result<TokenIntrospectionResponse> {
        let token_hash = self.hash_token(token)?;
        let grants = self.access_tokens.lock().unwrap();

        if let Some(grant) = grants.get(&token_hash) {
            if grant.client_id == client_id && Utc::now() <= grant.expires_at {
                return Ok(TokenIntrospectionResponse {
                    active: true,
                    client_id: Some(grant.client_id.clone()),
                    username: None, // Would need to fetch from user service
                    scope: Some(grant.scope.clone()),
                    exp: Some(grant.expires_at.timestamp()),
                    iat: None,
                    sub: Some(grant.user_id.to_string()),
                });
            }
        }

        Ok(TokenIntrospectionResponse {
            active: false,
            client_id: None,
            username: None,
            scope: None,
            exp: None,
            iat: None,
            sub: None,
        })
    }
}

#[derive(Debug, Clone)]
pub struct AccessTokenInfo {
    pub client_id: String,
    pub user_id: Uuid,
    pub scope: String,
    pub expires_at: DateTime<Utc>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TokenIntrospectionResponse {
    pub active: bool,
    pub client_id: Option<String>,
    pub username: Option<String>,
    pub scope: Option<String>,
    pub exp: Option<i64>,
    pub iat: Option<i64>,
    pub sub: Option<String>,
}

Testing and Security Validation

Comprehensive Security Tests

// tests/auth_security_tests.rs
#[cfg(test)]
mod tests {
    use super::*;
    use tempfile::TempDir;
    use tokio::time::{sleep, Duration};

    #[tokio::test]
    async fn test_jwt_security_vulnerabilities() {
        let temp_dir = TempDir::new().unwrap();
        let config = create_test_config(&temp_dir);
        let jwt_manager = create_test_jwt_manager();

        // Test 1: Ensure tokens expire
        let token_pair = jwt_manager.create_token_pair(
            Uuid::new_v4(),
            "testuser".to_string(),
            "test@example.com".to_string(),
            vec!["user".to_string()],
            vec!["read".to_string()],
            "read".to_string(),
            Uuid::new_v4().to_string(),
            None,
        ).unwrap();

        // Wait for token to expire (in real test, mock time)
        sleep(Duration::from_secs(2)).await;

        let result = jwt_manager.verify_token(&token_pair.access_token);
        assert!(result.is_err(), "Expired token should be rejected");

        // Test 2: Ensure token tampering is detected
        let mut tampered_token = token_pair.access_token.clone();
        tampered_token.push('x'); // Corrupt the token

        let result = jwt_manager.verify_token(&tampered_token);
        assert!(result.is_err(), "Tampered token should be rejected");

        // Test 3: Test algorithm confusion attack prevention
        let malicious_token = create_malicious_none_algorithm_token();
        let result = jwt_manager.verify_token(&malicious_token);
        assert!(result.is_err(), "None algorithm token should be rejected");
    }

    #[tokio::test]
    async fn test_mfa_security() {
        let mfa_manager = create_test_mfa_manager();
        let user_id = Uuid::new_v4();

        // Setup MFA
        let setup = mfa_manager.generate_mfa_setup(
            user_id,
            "testuser",
            "test@example.com"
        ).unwrap();

        let config = mfa_manager.enable_mfa(
            user_id,
            &setup.secret_key,
            &setup.backup_codes,
        ).unwrap();

        // Test 1: Invalid TOTP tokens are rejected
        let invalid_token = "123456";
        let result = mfa_manager.verify_totp_token(&config, invalid_token).unwrap();
        assert!(!result, "Invalid TOTP token should be rejected");

        // Test 2: Replay attacks are prevented
        let valid_token = generate_valid_totp_token(&setup.secret_key);
        let first_verify = mfa_manager.verify_totp_token(&config, &valid_token).unwrap();
        assert!(first_verify, "Valid TOTP token should be accepted");

        // Simulate time passing to prevent replay
        sleep(Duration::from_secs(31)).await;
        let replay_verify = mfa_manager.verify_totp_token(&config, &valid_token).unwrap();
        assert!(!replay_verify, "Replayed TOTP token should be rejected");

        // Test 3: Backup codes can only be used once
        let mut mutable_config = config.clone();
        let backup_code = setup.backup_codes[0].clone();

        let first_use = mfa_manager.verify_backup_code(&mut mutable_config, &backup_code).unwrap();
        assert!(first_use, "Valid backup code should be accepted");

        let second_use = mfa_manager.verify_backup_code(&mut mutable_config, &backup_code).unwrap();
        assert!(!second_use, "Used backup code should be rejected");
    }

    #[tokio::test]
    async fn test_oauth2_security() {
        let mut oauth2_provider = OAuth2Provider::new();
        let client = create_test_oauth2_client();
        oauth2_provider.register_client(client.clone());

        let user_id = Uuid::new_v4();

        // Test 1: Authorization code can only be used once
        let auth_request = AuthorizationRequest {
            response_type: "code".to_string(),
            client_id: client.client_id.clone(),
            redirect_uri: client.redirect_uris[0].clone(),
            scope: Some("read".to_string()),
            state: Some("test_state".to_string()),
            code_challenge: None,
            code_challenge_method: None,
        };

        let auth_response = oauth2_provider.authorize(&auth_request, user_id).unwrap();

        // First token exchange should succeed
        let token_request = TokenRequest {
            grant_type: "authorization_code".to_string(),
            code: Some(auth_response.code.clone()),
            redirect_uri: Some(client.redirect_uris[0].clone()),
            client_id: client.client_id.clone(),
            client_secret: Some("test-secret".to_string()),
            code_verifier: None,
            refresh_token: None,
            scope: None,
        };

        let jwt_manager = create_test_jwt_manager();
        let user_service = create_test_user_service();

        let first_exchange = oauth2_provider.exchange_code_for_token(
            &token_request,
            &jwt_manager,
            &user_service,
        );
        assert!(first_exchange.is_ok(), "First code exchange should succeed");

        // Second exchange with same code should fail
        let second_exchange = oauth2_provider.exchange_code_for_token(
            &token_request,
            &jwt_manager,
            &user_service,
        );
        assert!(second_exchange.is_err(), "Second code exchange should fail");

        // Test 2: PKCE validation
        let pkce_verifier = "test-code-verifier-that-is-long-enough";
        let pkce_challenge = generate_pkce_challenge(pkce_verifier);

        let auth_request_pkce = AuthorizationRequest {
            response_type: "code".to_string(),
            client_id: client.client_id.clone(),
            redirect_uri: client.redirect_uris[0].clone(),
            scope: Some("read".to_string()),
            state: Some("test_state".to_string()),
            code_challenge: Some(pkce_challenge),
            code_challenge_method: Some("S256".to_string()),
        };

        let auth_response_pkce = oauth2_provider.authorize(&auth_request_pkce, user_id).unwrap();

        // Token exchange with wrong verifier should fail
        let wrong_verifier_request = TokenRequest {
            grant_type: "authorization_code".to_string(),
            code: Some(auth_response_pkce.code.clone()),
            redirect_uri: Some(client.redirect_uris[0].clone()),
            client_id: client.client_id.clone(),
            client_secret: Some("test-secret".to_string()),
            code_verifier: Some("wrong-verifier".to_string()),
            refresh_token: None,
            scope: None,
        };

        let wrong_verifier_result = oauth2_provider.exchange_code_for_token(
            &wrong_verifier_request,
            &jwt_manager,
            &user_service,
        );
        assert!(wrong_verifier_result.is_err(), "Wrong PKCE verifier should be rejected");
    }

    #[tokio::test]
    async fn test_rate_limiting() {
        let rate_limiter = create_test_rate_limiter().await;
        let user_id = Uuid::new_v4();

        // Test multiple failed login attempts
        for i in 0..6 {
            let allowed = rate_limiter.check_rate_limit(user_id).await.unwrap();

            if i < 5 {
                assert!(allowed, "First 5 attempts should be allowed");
                rate_limiter.record_attempt(user_id, false).await.unwrap();
            } else {
                assert!(!allowed, "6th attempt should be rate limited");
            }
        }

        // Test that successful login resets the counter
        let user_id2 = Uuid::new_v4();
        for _ in 0..3 {
            let allowed = rate_limiter.check_rate_limit(user_id2).await.unwrap();
            assert!(allowed);
            rate_limiter.record_attempt(user_id2, false).await.unwrap();
        }

        // Successful login should reset
        rate_limiter.record_attempt(user_id2, true).await.unwrap();

        let allowed = rate_limiter.check_rate_limit(user_id2).await.unwrap();
        assert!(allowed, "Successful login should reset rate limit");
    }

    #[tokio::test]
    async fn test_session_security() {
        let session_store = create_test_session_store().await;
        let user_id = Uuid::new_v4();

        // Create session
        let session_id = session_store.create_session(
            user_id,
            ClientInfo {
                ip_address: "192.168.1.1".parse().unwrap(),
                user_agent: "Test Agent".to_string(),
            },
            Duration::hours(1),
        ).await.unwrap();

        // Test 1: Valid session should be retrievable
        let session = session_store.get_session(&session_id).await.unwrap();
        assert!(session.is_some());

        // Test 2: Expired sessions should be invalid
        session_store.expire_session(&session_id).await.unwrap();
        let expired_session = session_store.get_session(&session_id).await.unwrap();
        assert!(expired_session.is_none(), "Expired session should not be retrievable");

        // Test 3: Session fixation protection
        let old_session_id = session_store.create_session(
            user_id,
            ClientInfo {
                ip_address: "192.168.1.1".parse().unwrap(),
                user_agent: "Test Agent".to_string(),
            },
            Duration::hours(1),
        ).await.unwrap();

        let new_session_id = session_store.regenerate_session_id(&old_session_id).await.unwrap();

        // Old session should be invalid
        let old_session = session_store.get_session(&old_session_id).await.unwrap();
        assert!(old_session.is_none(), "Old session should be invalidated");

        // New session should be valid
        let new_session = session_store.get_session(&new_session_id).await.unwrap();
        assert!(new_session.is_some(), "New session should be valid");
    }

    #[tokio::test]
    async fn test_password_security() {
        let user_service = create_test_user_service();

        // Test 1: Weak passwords are rejected
        let weak_passwords = vec![
            "123456",
            "password",
            "qwerty",
            "abc123",
            "password123",
        ];

        for weak_password in weak_passwords {
            let result = user_service.validate_password_strength(weak_password);
            assert!(result.is_err(), "Weak password '{}' should be rejected", weak_password);
        }

        // Test 2: Strong passwords are accepted
        let strong_password = "MyS3cur3P@ssw0rd!2024";
        let result = user_service.validate_password_strength(strong_password);
        assert!(result.is_ok(), "Strong password should be accepted");

        // Test 3: Password hashing is secure
        let password = "test-password-123";
        let hash1 = user_service.hash_password(password).unwrap();
        let hash2 = user_service.hash_password(password).unwrap();

        // Same password should produce different hashes (salt)
        assert_ne!(hash1, hash2, "Password hashes should be different due to salt");

        // Both hashes should verify correctly
        assert!(user_service.verify_password(password, &hash1).unwrap());
        assert!(user_service.verify_password(password, &hash2).unwrap());

        // Wrong password should not verify
        assert!(!user_service.verify_password("wrong-password", &hash1).unwrap());
    }

    // Helper functions for tests
    fn create_test_jwt_manager() -> JwtManager {
        let secret = b"test-secret-key-that-is-long-enough-for-testing";
        let issuer = "test-issuer".to_string();
        let mut audience = HashSet::new();
        audience.insert("test-audience".to_string());

        JwtManager::new(
            secret,
            issuer,
            audience,
            Duration::seconds(1), // Short expiry for testing
            Duration::hours(1),
        ).unwrap()
    }

    fn create_test_mfa_manager() -> MfaManager {
        let encryption_key = [0u8; 32]; // In real tests, use proper key
        MfaManager::new(encryption_key, "Test App".to_string())
    }

    fn create_test_oauth2_client() -> OAuth2Client {
        OAuth2Client {
            client_id: "test-client".to_string(),
            client_secret_hash: hash_client_secret("test-secret"),
            client_name: "Test Client".to_string(),
            redirect_uris: vec!["https://example.com/callback".to_string()],
            allowed_scopes: vec!["read".to_string(), "write".to_string()],
            is_confidential: true,
            token_lifetime_seconds: 3600,
            refresh_token_lifetime_seconds: 86400,
        }
    }

    async fn create_test_rate_limiter() -> MfaRateLimiter {
        // Use Redis test instance or mock
        MfaRateLimiter::new("redis://localhost:6379").unwrap()
    }

    async fn create_test_session_store() -> SessionStore {
        // Create test session store
        SessionStore::new("redis://localhost:6379").await.unwrap()
    }

    fn create_test_user_service() -> UserService {
        // Create test user service with in-memory database
        UserService::new_with_test_db()
    }
}

Perfect! I’ve created comprehensive blog posts covering the complete Rust security tooling development journey. Let me continue with the next blog post to complete the series.

<function_calls> [{“id”: “blog-1”, “content”: “Write eBPF Fundamentals for Rust Security Engineers”, “status”: “completed”, “priority”: “high”}, {“id”: “blog-2”, “content”: “Write Cross-Platform Security Service Architecture in Rust”, “status”: “completed”, “priority”: “high”}, {“id”: “blog-3”, “content”: “Write Building Production eBPF Security Monitors”, “status”: “completed”, “priority”: “high”}, {“id”: “blog-4”, “content”: “Write Secure Authentication Systems in Rust”, “status”: “completed”, “priority”: “high”}, {“id”: “blog-5”, “content”: “Write High-Performance Security Event Processing”, “status”: “in_progress”, “priority”: “medium”}, {“id”: “blog-6”, “content”: “Write System Service Security Hardening”, “status”: “pending”, “priority”: “medium”}, {“id”: “blog-7”, “content”: “Write Distributed Security Analytics Architecture”, “status”: “pending”, “priority”: “medium”}, {“id”: “blog-8”, “content”: “Write eBPF Security Policy Enforcement”, “status”: “pending”, “priority”: “low”}, {“id”: “blog-9”, “content”: “Write Enterprise Security Tool Deployment”, “status”: “pending”, “priority”: “low”}]