diff --git a/Cargo.toml b/Cargo.toml index cd9c8b9..bfb5e07 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] actix-web = "4.0.1" +actix-web-httpauth = "0.8.2" async-trait = "0.1.82" base64 = "0.22.1" constant_time_eq = "0.3.1" diff --git a/src/auth_middleware.rs b/src/auth_middleware.rs new file mode 100644 index 0000000..f3f3833 --- /dev/null +++ b/src/auth_middleware.rs @@ -0,0 +1,21 @@ +use crate::core::token::Claims; +use actix_web::dev::ServiceRequest; +use actix_web::Error; +use actix_web_httpauth::extractors::bearer::BearerAuth; +use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation}; + +async fn authentication_middleware( + req: ServiceRequest, + credentials: BearerAuth, +) -> Result { + let token = credentials.token(); + + // Replace this with actual token validation logic (e.g., checking JWT or database) + let decoding_key = DecodingKey::from_secret(b"your_secret_key"); + let validation = Validation::new(Algorithm::HS256); + + match decode::(token, &decoding_key, &validation) { + Ok(_) => Ok(req), + Err(_) => Err(actix_web::error::ErrorUnauthorized("Invalid token")), + } +} diff --git a/src/endpoints/register.rs b/src/endpoints/register.rs new file mode 100644 index 0000000..22f9987 --- /dev/null +++ b/src/endpoints/register.rs @@ -0,0 +1,143 @@ +use actix_web_httpauth::extractors::bearer::BearerAuth; +use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation}; +use actix_web::{web, HttpResponse, Responder}; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use std::sync::Mutex; +use uuid::Uuid; +use bcrypt::{hash, DEFAULT_COST}; +use chrono::Utc; + + +#[derive(Deserialize, Serialize, Debug, Clone)] +pub struct ClientMetadata { + pub client_name: String, + pub redirect_uris: Vec, + pub grant_types: Vec, + pub response_types: Vec, + pub software_statement: Option, // Optional JWT field +} + +#[derive(Serialize, Debug, Clone)] +pub struct ClientRegistrationResponse { + pub client_id: String, + pub client_secret: String, +} + +#[derive(Clone)] +pub struct ClientStore { + pub clients: Mutex>, // Store client metadata securely + pub client_secrets: Mutex>, // Store client secrets securely (hashed) +} + +impl ClientStore { + pub fn new() -> Self { + Self { + clients: Mutex::new(HashMap::new()), + client_secrets: Mutex::new(HashMap::new()), + } + } + + // Register client and store metadata and secret + pub fn register_client( + &self, + metadata: ClientMetadata, + ) -> Result { + let client_id = Uuid::new_v4().to_string(); + let client_secret = Uuid::new_v4().to_string(); + + // Hash the client_secret before storing it + let hashed_secret = hash(&client_secret, DEFAULT_COST).map_err(|_| "hashing_error")?; + + { + let mut clients = self.clients.lock().unwrap(); + clients.insert(client_id.clone(), metadata); + } + + { + let mut secrets = self.client_secrets.lock().unwrap(); + secrets.insert(client_id.clone(), hashed_secret); + } + + Ok(ClientRegistrationResponse { + client_id, + client_secret, + }) + } +} + + + + +// The /register endpoint +async fn register_client( + client_store: web::Data, + metadata: web::Json, +) -> impl Responder { + // Validate metadata + if metadata.redirect_uris.is_empty() { + return HttpResponse::BadRequest().body("redirect_uris is required"); + } + + if metadata.grant_types.is_empty() { + return HttpResponse::BadRequest().body("grant_types is required"); + } + + if metadata.response_types.is_empty() { + return HttpResponse::BadRequest().body("response_types is required"); + } + + // Validate the software statement if provided + if let Some(software_statement) = &metadata.software_statement { + if let Err(_) = validate_software_statement(software_statement) { + return HttpResponse::BadRequest().body("Invalid software statement"); + } + } + + // Register the client + match client_store.register_client(metadata.into_inner()) { + Ok(response) => { + log_registration_attempt(true, &metadata); + HttpResponse::Ok().json(response) + } + Err(_) => { + log_registration_attempt(false, &metadata); + HttpResponse::InternalServerError().body("Failed to register client") + } + } +} + +// Log the registration attempt +pub fn log_registration_attempt(success: bool, metadata: &ClientMetadata) { + if success { + println!("Client registered successfully: {:?}", metadata); + } else { + println!("Failed registration attempt: {:?}", metadata); + } +} + +// Update client metadata +async fn update_client( + client_store: web::Data, + client_id: web::Path, + updated_metadata: web::Json, +) -> impl Responder { + let mut clients = client_store.clients.lock().unwrap(); + + if let Some(existing_client) = clients.get_mut(&client_id.into_inner()) { + // Update the client metadata + *existing_client = updated_metadata.into_inner(); + return HttpResponse::Ok().body("Client updated"); + } + + HttpResponse::NotFound().body("Client not found") +} + +pub fn log_registration_attempt(success: bool, metadata: &ClientMetadata) { + let timestamp = Utc::now().to_rfc3339(); + if success { + println!("[INFO] [{}] Client registered successfully: {:?}", timestamp, metadata); + } else { + println!("[ERROR] [{}] Failed registration attempt: {:?}", timestamp, metadata); + } +} diff --git a/src/endpoints/token.rs b/src/endpoints/token.rs index 433896d..fb77a69 100644 --- a/src/endpoints/token.rs +++ b/src/endpoints/token.rs @@ -3,6 +3,7 @@ use crate::core::extension_grants::CustomGrant; use crate::core::extension_grants::DeviceFlowHandler; use crate::core::extension_grants::ExtensionGrantHandler; use crate::core::pkce::validate_pkce_challenge; +use crate::core::token; use crate::core::token::TokenRevocation; use crate::core::types::{TokenError, TokenRequest, TokenResponse}; use crate::endpoints::revoke::RevokeTokenRequest; @@ -150,14 +151,15 @@ pub fn sign_token(private_key: &[u8], payload: &Claims) -> Result, + pub aud: Option, + pub client_id: Option, + pub iat: u64, + pub iss: Option, } // Token revocation endpoint handler diff --git a/src/lib.rs b/src/lib.rs index 1c11c65..fe2e63f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,6 +5,7 @@ use std::sync::{Arc, Mutex}; use std::time::Duration; // Expose the core, security, endpoints, and storage modules +pub mod auth_middleware; pub mod authentication; pub mod core; pub mod endpoints;