use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SsoProvider {
pub id: Uuid,
pub org_id: Uuid,
pub name: String,
pub issuer_url: String,
pub client_id: String,
pub client_secret_encrypted: String,
pub scopes: Vec<String>,
pub enabled: bool,
pub allow_registration: bool,
pub email_domain: Option<String>,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
impl SsoProvider {
pub fn new(
org_id: Uuid,
name: String,
issuer_url: String,
client_id: String,
client_secret_encrypted: String,
) -> Self {
let now = Utc::now();
Self {
id: Uuid::new_v4(),
org_id,
name,
issuer_url,
client_id,
client_secret_encrypted,
scopes: vec!["openid".into(), "email".into(), "profile".into()],
enabled: true,
allow_registration: true,
email_domain: None,
created_at: now,
updated_at: now,
}
}
pub fn scopes_string(&self) -> String {
self.scopes.join(" ")
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SsoAuthState {
pub state_id: Uuid,
pub provider_id: Uuid,
pub org_id: Uuid,
pub pkce_verifier: String,
pub nonce: String,
pub redirect_uri: Option<String>,
pub access_code: Option<String>,
pub referral: Option<String>,
pub created_at: DateTime<Utc>,
pub expires_at: DateTime<Utc>,
}
impl SsoAuthState {
pub fn new(
provider_id: Uuid,
org_id: Uuid,
pkce_verifier: String,
nonce: String,
redirect_uri: Option<String>,
ttl_seconds: i64,
) -> Self {
let now = Utc::now();
Self {
state_id: Uuid::new_v4(),
provider_id,
org_id,
pkce_verifier,
nonce,
redirect_uri,
access_code: None,
referral: None,
created_at: now,
expires_at: now + chrono::Duration::seconds(ttl_seconds),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sso_provider_creation() {
let org_id = Uuid::new_v4();
let provider = SsoProvider::new(
org_id,
"Okta".into(),
"https://dev-123.okta.com".into(),
"client-id".into(),
"encrypted-secret".into(),
);
assert_eq!(provider.org_id, org_id);
assert_eq!(provider.name, "Okta");
assert!(provider.enabled);
assert_eq!(provider.scopes_string(), "openid email profile");
}
#[test]
fn test_sso_auth_state_expiry() {
let state = SsoAuthState::new(
Uuid::new_v4(),
Uuid::new_v4(),
"verifier".into(),
"nonce".into(),
None,
300,
);
assert!(state.expires_at > state.created_at);
}
#[test]
fn test_sso_auth_state_defaults_to_no_access_code() {
let state = SsoAuthState::new(
Uuid::new_v4(),
Uuid::new_v4(),
"verifier".into(),
"nonce".into(),
None,
300,
);
assert!(state.access_code.is_none());
}
}