#![allow(clippy::result_large_err)]
use pandrs::auth::{
ApiKeyInfo, AuthEvent, AuthEventType, AuthManager, AuthMethod, JwtConfig, OAuthClient,
OAuthClientInfo, OAuthConfig, OAuthGrantType, RefreshToken, Session, TokenClaims, TokenRequest,
UserInfo,
};
use pandrs::error::Result;
use pandrs::multitenancy::Permission;
use std::collections::HashMap;
use std::time::{Duration, SystemTime};
fn main() -> Result<()> {
println!("๐ PandRS Security: JWT & OAuth2 Authentication Example");
println!("========================================================\n");
println!("๐ Scenario 1: JWT Token Authentication");
jwt_authentication_flow()?;
println!("\n๐ Scenario 2: OAuth2 Authorization Code Flow with PKCE");
oauth_authorization_code_flow()?;
println!("\n๐ค Scenario 3: OAuth2 Client Credentials (Service-to-Service)");
oauth_client_credentials_flow()?;
println!("\n๐ซ Scenario 4: API Key Authentication");
api_key_authentication_flow()?;
println!("\nโฐ Scenario 5: Session Management");
session_management_flow()?;
println!("\n๐ Scenario 6: Token Refresh Mechanism");
token_refresh_flow()?;
println!("\n๐ข Scenario 7: Multi-Tenant Authentication");
multi_tenant_authentication()?;
println!("\n๐ Scenario 8: Security Audit Trail");
security_audit_trail()?;
println!("\n๐ฆ Scenario 9: Rate Limiting and Security Controls");
rate_limiting_controls()?;
println!("\nโ
All authentication scenarios completed successfully!");
Ok(())
}
fn jwt_authentication_flow() -> Result<()> {
let jwt_config = JwtConfig::new(b"super_secret_key_for_production_use_random_256_bits")
.with_issuer("pandrs-analytics-platform")
.with_audience("analytics-api")
.with_expiration(3600);
let mut auth_manager = AuthManager::new(jwt_config);
println!(" ๐ค Registering user 'alice@company-a.com' for tenant 'company_a'");
let user = UserInfo::new("alice", "alice@company-a.com", "company_a")
.with_display_name("Alice Johnson")
.with_password("secure_password_123")
.with_role("analyst")
.with_permission(Permission::Read)
.with_permission(Permission::Write);
auth_manager.register_user(user)?;
println!(" ๐ Authenticating with email and password...");
let auth_result =
auth_manager.authenticate_password("alice@company-a.com", "secure_password_123")?;
println!(" โ
Authentication successful!");
println!(" User ID: {}", auth_result.user_id);
println!(" Tenant: {}", auth_result.tenant_id);
println!(" Permissions: {:?}", auth_result.permissions);
println!(" Session ID: {:?}", auth_result.session_id);
println!("\n ๐๏ธ Generating JWT token...");
let token = auth_manager.generate_token("alice")?;
println!(" โ
JWT token generated (length: {} chars)", token.len());
println!(" Token preview: {}...", &token[..50]);
println!("\n โ๏ธ Validating JWT token...");
let validated = auth_manager.validate_token(&token)?;
println!(" โ
Token is valid!");
println!(" Subject: {}", validated.user_id);
println!(" Expires at: {}", validated.expires_at);
println!("\n โ Testing invalid credentials...");
match auth_manager.authenticate_password("alice@company-a.com", "wrong_password") {
Ok(_) => println!(" โ ๏ธ Unexpected success with wrong password!"),
Err(_) => println!(" โ
Correctly rejected invalid password"),
}
Ok(())
}
fn oauth_authorization_code_flow() -> Result<()> {
let oauth_config = OAuthConfig::new(
"https://auth.pandrs-analytics.com",
"pandrs_web_client",
"client_secret_abc123",
)
.with_redirect_uri("https://app.pandrs-analytics.com/callback")
.with_scope("openid")
.with_scope("profile")
.with_scope("analytics:read");
let mut oauth_client = OAuthClient::new(oauth_config);
println!(" ๐ Registering OAuth client for tenant 'company_a'");
let client_info = OAuthClientInfo {
client_id: "pandrs_web_client".to_string(),
client_secret_hash: hash_secret("client_secret_abc123"),
redirect_uris: vec!["https://app.pandrs-analytics.com/callback".to_string()],
grant_types: vec![OAuthGrantType::AuthorizationCode],
scopes: vec![
"openid".to_string(),
"profile".to_string(),
"analytics:read".to_string(),
],
tenant_id: "company_a".to_string(),
};
oauth_client.register_client(client_info);
println!(" ๐ Step 1: User authorizes application...");
let auth_code = oauth_client.create_authorization_code(
"pandrs_web_client",
"https://app.pandrs-analytics.com/callback",
vec!["openid".to_string(), "profile".to_string()],
"alice",
None, None,
)?;
println!(" โ
Authorization code generated: {}...", &auth_code[..16]);
println!("\n ๐ Step 2: Exchanging authorization code for access token...");
let token_response = oauth_client.exchange_code(
"pandrs_web_client",
"client_secret_abc123",
&auth_code,
"https://app.pandrs-analytics.com/callback",
None,
)?;
println!(" โ
Access token received!");
println!(" Token type: {}", token_response.token_type);
println!(" Expires in: {} seconds", token_response.expires_in);
println!(" Scopes: {:?}", token_response.scope);
println!(
" Has refresh token: {}",
token_response.refresh_token.is_some()
);
println!("\n โ๏ธ Step 3: Introspecting access token...");
let introspection = oauth_client.introspect_token(&token_response.access_token);
println!(" โ
Token introspection result:");
println!(" Active: {}", introspection.active);
println!(" Client ID: {:?}", introspection.client_id);
println!(" Username: {:?}", introspection.username);
Ok(())
}
fn oauth_client_credentials_flow() -> Result<()> {
let oauth_config = OAuthConfig::new(
"https://auth.pandrs-analytics.com",
"analytics_service",
"service_secret_xyz789",
);
let mut oauth_client = OAuthClient::new(oauth_config);
println!(" ๐ค Registering service client 'analytics_service'");
let service_client = OAuthClientInfo {
client_id: "analytics_service".to_string(),
client_secret_hash: hash_secret("service_secret_xyz789"),
redirect_uris: vec![],
grant_types: vec![OAuthGrantType::ClientCredentials],
scopes: vec![
"analytics:read".to_string(),
"analytics:write".to_string(),
"reports:generate".to_string(),
],
tenant_id: "company_a".to_string(),
};
oauth_client.register_client(service_client);
println!(" ๐ซ Requesting access token with client credentials...");
let token_response = oauth_client.client_credentials_grant(
"analytics_service",
"service_secret_xyz789",
Some(vec![
"analytics:read".to_string(),
"reports:generate".to_string(),
]),
)?;
println!(" โ
Service token obtained!");
println!(
" Access token: {}...",
&token_response.access_token[..20]
);
println!(" Token type: {}", token_response.token_type);
println!(" Expires in: {} seconds", token_response.expires_in);
println!(" Granted scopes: {:?}", token_response.scope);
println!(
" Has refresh token: {} (not issued for client credentials)",
token_response.refresh_token.is_some()
);
println!("\n โ๏ธ Verifying service token...");
let introspection = oauth_client.introspect_token(&token_response.access_token);
if introspection.active {
println!(" โ
Service token is active and valid");
println!(" Client: {:?}", introspection.client_id);
println!(" Scopes: {:?}", introspection.scope);
}
Ok(())
}
fn api_key_authentication_flow() -> Result<()> {
let mut auth_manager = AuthManager::new(JwtConfig::default());
let user = UserInfo::new("bob", "bob@company-b.com", "company_b")
.with_password("bob_password")
.with_permission(Permission::Read)
.with_permission(Permission::Write);
auth_manager.register_user(user)?;
println!(" ๐ Creating API key for user 'bob'...");
let api_key = auth_manager.create_api_key(
"bob",
"Production API Key",
Some(vec![Permission::Read, Permission::Write]),
)?;
println!(" โ
API key created: {}...", &api_key[..20]);
println!(" โ ๏ธ Store this securely - it won't be shown again!");
println!("\n ๐ Authenticating with API key...");
let auth_result = auth_manager.authenticate_api_key(&api_key)?;
println!(" โ
API key authentication successful!");
println!(" User: {}", auth_result.user_id);
println!(" Tenant: {}", auth_result.tenant_id);
println!(" Permissions: {:?}", auth_result.permissions);
println!("\n ๐ API key usage tracking:");
for i in 1..=3 {
match auth_manager.authenticate_api_key(&api_key) {
Ok(result) => {
println!(
" Request {}: โ
Authenticated as '{}'",
i, result.user_id
);
}
Err(e) => println!(" Request {}: โ Failed: {}", i, e),
}
}
println!("\n ๐ซ Revoking API key...");
auth_manager.revoke_api_key(&api_key)?;
println!(" โ
API key revoked");
println!("\n โ๏ธ Testing revoked key...");
match auth_manager.authenticate_api_key(&api_key) {
Ok(_) => println!(" โ ๏ธ Unexpected success with revoked key!"),
Err(_) => println!(" โ
Revoked key correctly rejected"),
}
Ok(())
}
fn session_management_flow() -> Result<()> {
let mut auth_manager =
AuthManager::new(JwtConfig::default()).with_session_timeout(Duration::from_secs(300));
let user = UserInfo::new("carol", "carol@company-c.com", "company_c")
.with_password("carol_password")
.with_permission(Permission::Read);
auth_manager.register_user(user)?;
println!(" ๐ Creating session for user 'carol'...");
let auth_result =
auth_manager.authenticate_password("carol@company-c.com", "carol_password")?;
let session_id = auth_result
.session_id
.ok_or_else(|| pandrs::error::Error::InvalidOperation("No session created".to_string()))?;
println!(" โ
Session created: {}", session_id);
println!("\n โ๏ธ Validating session...");
let session = auth_manager.validate_session(&session_id)?;
println!(" โ
Session is valid");
println!(" User: {}", session.user_id);
println!(" Created: {:?}", session.created_at);
println!("\n ๐ Simulating API requests with session...");
for i in 1..=3 {
match auth_manager.validate_session(&session_id) {
Ok(s) => println!(" Request {}: โ
Session valid (user: {})", i, s.user_id),
Err(e) => println!(" Request {}: โ Session error: {}", i, e),
}
}
println!("\n ๐ Logging out (invalidating session)...");
auth_manager.logout(&session_id)?;
println!(" โ
Session invalidated");
println!("\n โ๏ธ Testing invalidated session...");
match auth_manager.validate_session(&session_id) {
Ok(_) => println!(" โ ๏ธ Unexpected success with invalidated session!"),
Err(_) => println!(" โ
Invalidated session correctly rejected"),
}
Ok(())
}
fn token_refresh_flow() -> Result<()> {
let mut auth_manager = AuthManager::new(JwtConfig::default())
.with_token_expiry(Duration::from_secs(300)) .with_refresh_token_expiry(Duration::from_secs(86400));
let user = UserInfo::new("dave", "dave@company-d.com", "company_d")
.with_password("dave_password")
.with_permission(Permission::Read);
auth_manager.register_user(user)?;
println!(" ๐ Initial authentication...");
auth_manager.authenticate_password("dave@company-d.com", "dave_password")?;
println!(" ๐๏ธ Generating access token and refresh token...");
let access_token = auth_manager.generate_token("dave")?;
let refresh_token = auth_manager.generate_refresh_token("dave")?;
println!(" โ
Tokens generated");
println!(" Access token: {}...", &access_token[..30]);
println!(" Refresh token: {}...", &refresh_token[..30]);
println!("\n โฐ Simulating access token expiration...");
println!(" ๐ Using refresh token to get new access token...");
let new_access_token = auth_manager.refresh_access_token(&refresh_token)?;
println!(
" โ
New access token obtained: {}...",
&new_access_token[..30]
);
println!("\n โ๏ธ Validating new access token...");
let validated = auth_manager.validate_token(&new_access_token)?;
println!(" โ
New token is valid");
println!(" User: {}", validated.user_id);
println!("\n ๐ซ Revoking refresh token...");
auth_manager.revoke_refresh_token(&refresh_token)?;
println!(" โ
Refresh token revoked");
println!("\n โ๏ธ Testing revoked refresh token...");
match auth_manager.refresh_access_token(&refresh_token) {
Ok(_) => println!(" โ ๏ธ Unexpected success with revoked refresh token!"),
Err(_) => println!(" โ
Revoked refresh token correctly rejected"),
}
Ok(())
}
fn multi_tenant_authentication() -> Result<()> {
let mut auth_manager = AuthManager::new(JwtConfig::default());
println!(" ๐ข Setting up multi-tenant environment...");
let user_a = UserInfo::new("alice", "alice@company-a.com", "tenant_a")
.with_password("password_a")
.with_permission(Permission::Read)
.with_permission(Permission::Write);
auth_manager.register_user(user_a)?;
let user_b = UserInfo::new("bob", "bob@company-b.com", "tenant_b")
.with_password("password_b")
.with_permission(Permission::Read);
auth_manager.register_user(user_b)?;
println!(" โ
Registered users for tenant_a and tenant_b");
println!("\n ๐ Authenticating users from different tenants...");
let auth_a = auth_manager.authenticate_password("alice@company-a.com", "password_a")?;
println!(" โ
Tenant A user authenticated:");
println!(
" User: {} (Tenant: {})",
auth_a.user_id, auth_a.tenant_id
);
println!(" Permissions: {:?}", auth_a.permissions);
let auth_b = auth_manager.authenticate_password("bob@company-b.com", "password_b")?;
println!(" โ
Tenant B user authenticated:");
println!(
" User: {} (Tenant: {})",
auth_b.user_id, auth_b.tenant_id
);
println!(" Permissions: {:?}", auth_b.permissions);
println!("\n ๐๏ธ Generating tenant-scoped JWT tokens...");
let token_a = auth_manager.generate_token("alice")?;
let token_b = auth_manager.generate_token("bob")?;
println!("\n โ๏ธ Validating tenant isolation...");
let validated_a = auth_manager.validate_token(&token_a)?;
let validated_b = auth_manager.validate_token(&token_b)?;
println!(" โ
Tenant isolation verified:");
println!(" Token A tenant: {}", validated_a.tenant_id);
println!(" Token B tenant: {}", validated_b.tenant_id);
println!(
" Tenants are separate: {}",
validated_a.tenant_id != validated_b.tenant_id
);
Ok(())
}
fn security_audit_trail() -> Result<()> {
let mut auth_manager = AuthManager::new(JwtConfig::default());
let user = UserInfo::new("eve", "eve@company-e.com", "company_e")
.with_password("eve_password")
.with_permission(Permission::Read);
auth_manager.register_user(user)?;
println!(" ๐ Generating authentication events...");
auth_manager.authenticate_password("eve@company-e.com", "eve_password")?;
let _ = auth_manager.authenticate_password("eve@company-e.com", "wrong_password");
let token = auth_manager.generate_token("eve")?;
auth_manager.validate_token(&token)?;
auth_manager.create_api_key("eve", "Test Key", None)?;
println!("\n ๐ Security Audit Trail:");
let events = auth_manager.get_user_events("eve");
for (i, event) in events.iter().enumerate() {
println!(" Event {}: {:?}", i + 1, event.event_type);
println!(" Time: {:?}", event.timestamp);
println!(" Method: {:?}", event.auth_method);
println!(" Success: {}", event.success);
if let Some(ref msg) = event.error_message {
println!(" Error: {}", msg);
}
println!();
}
println!(" โ
Total events logged: {}", events.len());
Ok(())
}
fn rate_limiting_controls() -> Result<()> {
println!(" ๐ฆ Security controls demonstration");
println!(" - Rate limiting prevents abuse");
println!(" - IP whitelisting restricts access");
println!(" - Automatic cleanup of expired tokens");
println!(" - Session timeout enforcement");
let mut auth_manager = AuthManager::new(JwtConfig::default())
.with_token_expiry(Duration::from_secs(60))
.with_session_timeout(Duration::from_secs(300));
let user = UserInfo::new("frank", "frank@company-f.com", "company_f")
.with_password("frank_password")
.with_permission(Permission::Read);
auth_manager.register_user(user)?;
println!("\n โณ Creating test sessions...");
for i in 1..=3 {
auth_manager.authenticate_password("frank@company-f.com", "frank_password")?;
println!(" Session {} created", i);
}
println!("\n ๐งน Running cleanup of expired credentials...");
auth_manager.cleanup_expired();
println!(" โ
Cleanup completed");
println!("\n ๐ Demonstrating password change...");
auth_manager.change_password("frank", "frank_password", "new_secure_password")?;
println!(" โ
Password changed successfully");
println!(" All refresh tokens have been revoked for security");
match auth_manager.authenticate_password("frank@company-f.com", "frank_password") {
Ok(_) => println!(" โ ๏ธ Old password still works - security issue!"),
Err(_) => println!(" โ
Old password correctly rejected"),
}
match auth_manager.authenticate_password("frank@company-f.com", "new_secure_password") {
Ok(_) => println!(" โ
New password works correctly"),
Err(_) => println!(" โ ๏ธ New password doesn't work - security issue!"),
}
Ok(())
}
fn hash_secret(secret: &str) -> String {
use sha2::{Digest, Sha256};
let mut hasher = Sha256::new();
hasher.update(secret.as_bytes());
let result = hasher.finalize();
result.iter().map(|b| format!("{:02x}", b)).collect()
}