use crate::errors::Result;
use crate::server::core::client_registry::{ClientConfig, ClientRegistry};
use crate::storage::core::AuthStorage;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OAuth2ServerConfig {
pub issuer: String,
pub supported_scopes: Vec<String>,
pub supported_response_types: Vec<String>,
pub supported_grant_types: Vec<String>,
}
impl Default for OAuth2ServerConfig {
fn default() -> Self {
Self {
issuer: "https://auth.example.com".to_string(), supported_scopes: vec![
"openid".to_string(),
"profile".to_string(),
"email".to_string(),
"address".to_string(),
"phone".to_string(),
"offline_access".to_string(),
],
supported_response_types: vec![
"code".to_string(),
"token".to_string(),
"id_token".to_string(),
"code token".to_string(),
"code id_token".to_string(),
"token id_token".to_string(),
"code token id_token".to_string(),
],
supported_grant_types: vec![
"authorization_code".to_string(),
"implicit".to_string(),
"password".to_string(),
"client_credentials".to_string(),
"refresh_token".to_string(),
"urn:ietf:params:oauth:grant-type:device_code".to_string(),
"urn:ietf:params:oauth:grant-type:token-exchange".to_string(),
],
}
}
}
#[derive(Clone)]
pub struct OAuth2Server {
config: OAuth2ServerConfig,
client_registry: Arc<ClientRegistry>,
}
impl OAuth2Server {
pub async fn new(storage: Arc<dyn AuthStorage>) -> Result<Self> {
let client_registry = Arc::new(ClientRegistry::new(storage).await?);
let config = OAuth2ServerConfig::default();
Ok(Self {
config,
client_registry,
})
}
pub async fn new_with_config(
storage: Arc<dyn AuthStorage>,
config: OAuth2ServerConfig,
) -> Result<Self> {
let client_registry = Arc::new(ClientRegistry::new(storage).await?);
Ok(Self {
config,
client_registry,
})
}
pub async fn register_client(&self, config: ClientConfig) -> Result<ClientConfig> {
self.client_registry.register_client(config).await
}
pub async fn get_client(&self, client_id: &str) -> Result<Option<ClientConfig>> {
self.client_registry.get_client(client_id).await
}
pub async fn update_client(&self, client_id: &str, config: ClientConfig) -> Result<()> {
self.client_registry.update_client(client_id, config).await
}
pub async fn delete_client(&self, client_id: &str) -> Result<()> {
self.client_registry.delete_client(client_id).await
}
pub async fn get_server_configuration(&self) -> Result<serde_json::Value> {
let issuer = &self.config.issuer;
let config = serde_json::json!({
"issuer": issuer,
"authorization_endpoint": format!("{}/oauth/authorize", issuer),
"token_endpoint": format!("{}/oauth/token", issuer),
"scopes_supported": self.config.supported_scopes,
"response_types_supported": self.config.supported_response_types,
"grant_types_supported": self.config.supported_grant_types,
"revocation_endpoint": format!("{}/oauth/revoke", issuer),
"introspection_endpoint": format!("{}/oauth/introspect", issuer)
});
Ok(config)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::server::core::client_registry::{ClientConfig, ClientType};
use crate::storage::memory::InMemoryStorage;
#[tokio::test]
async fn test_oauth2_server_creation() {
let storage = Arc::new(InMemoryStorage::new());
let server = OAuth2Server::new(storage).await.unwrap();
let client_config = ClientConfig {
client_id: "test_client".to_string(),
client_type: ClientType::Public,
redirect_uris: vec!["https://example.com/callback".to_string()],
..Default::default()
};
let registered_client = server.register_client(client_config).await.unwrap();
assert_eq!(registered_client.client_id, "test_client");
let retrieved_client = server.get_client("test_client").await.unwrap().unwrap();
assert_eq!(retrieved_client.client_id, "test_client");
}
}