mannequin/src/oidc/mod.rs
use chrono::Utc;
use serde::{Deserialize, Serialize};
use std::sync::Mutex;
use uuid::Uuid;
/// Mock OIDC provider for testing authentication flows.
pub struct OidcService {
issuer: String,
tokens: Mutex<Vec<TokenRecord>>,
}
#[derive(Clone, Serialize)]
pub struct TokenRecord {
pub access_token: String,
pub subject: String,
pub issued_at: i64,
pub expires_at: i64,
}
#[derive(Serialize)]
pub struct OidcDiscovery {
pub issuer: String,
pub authorization_endpoint: String,
pub token_endpoint: String,
pub userinfo_endpoint: String,
pub jwks_uri: String,
}
#[derive(Deserialize)]
pub struct TokenRequest {
pub grant_type: Option<String>,
pub username: Option<String>,
pub password: Option<String>,
pub client_id: Option<String>,
}
#[derive(Serialize)]
pub struct TokenResponse {
pub access_token: String,
pub token_type: String,
pub expires_in: i64,
pub sub: String,
}
impl OidcService {
pub fn new(issuer: String) -> Self {
Self {
issuer,
tokens: Mutex::new(Vec::new()),
}
}
pub fn discovery(&self) -> OidcDiscovery {
OidcDiscovery {
issuer: self.issuer.clone(),
authorization_endpoint: format!("{}/authorize", self.issuer),
token_endpoint: format!("{}/token", self.issuer),
userinfo_endpoint: format!("{}/userinfo", self.issuer),
jwks_uri: format!("{}/jwks", self.issuer),
}
}
/// Issue a token (accepts any credentials — it's a test dummy).
pub fn issue_token(&self, subject: &str) -> TokenResponse {
let access_token = Uuid::new_v4().to_string();
let now = Utc::now().timestamp();
let expires_in = 3600;
self.tokens.lock().unwrap().push(TokenRecord {
access_token: access_token.clone(),
subject: subject.to_string(),
issued_at: now,
expires_at: now + expires_in,
});
TokenResponse {
access_token,
token_type: "Bearer".to_string(),
expires_in,
sub: subject.to_string(),
}
}
/// Validate a token (returns subject if valid).
pub fn validate_token(&self, token: &str) -> Option<String> {
let now = Utc::now().timestamp();
self.tokens
.lock()
.unwrap()
.iter()
.find(|t| t.access_token == token && t.expires_at > now)
.map(|t| t.subject.clone())
}
}