Skip to content

Commit

Permalink
Add authentication middleware for JWT validation in client registrati…
Browse files Browse the repository at this point in the history
…on flow

- Implement middleware for validating Bearer tokens using JWT.
- Integrated  for Bearer token extraction.
- Added JWT token decoding and validation logic using .
- Updated  to include  and  dependencies.
  • Loading branch information
Mehrn0ush committed Sep 24, 2024
1 parent ee37bc7 commit 5108f48
Show file tree
Hide file tree
Showing 5 changed files with 175 additions and 7 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
21 changes: 21 additions & 0 deletions src/auth_middleware.rs
Original file line number Diff line number Diff line change
@@ -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<ServiceRequest, Error> {
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::<Claims>(token, &decoding_key, &validation) {
Ok(_) => Ok(req),
Err(_) => Err(actix_web::error::ErrorUnauthorized("Invalid token")),
}
}
143 changes: 143 additions & 0 deletions src/endpoints/register.rs
Original file line number Diff line number Diff line change
@@ -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<String>,
pub grant_types: Vec<String>,
pub response_types: Vec<String>,
pub software_statement: Option<String>, // 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<HashMap<String, ClientMetadata>>, // Store client metadata securely
pub client_secrets: Mutex<HashMap<String, String>>, // 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<ClientRegistrationResponse, &'static str> {
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<ClientStore>,
metadata: web::Json<ClientMetadata>,
) -> 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<ClientStore>,
client_id: web::Path<String>,
updated_metadata: web::Json<ClientMetadata>,
) -> 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);
}
}
16 changes: 9 additions & 7 deletions src/endpoints/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -150,14 +151,15 @@ pub fn sign_token(private_key: &[u8], payload: &Claims) -> Result<String, TokenE
encode(&header, payload, &encoding_key).map_err(|_| TokenError::InternalError)
}

// Example Claims structure for JWT tokens
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone, Default)]
pub struct Claims {
sub: String, // Subject (user_id)
client_id: String, // Client ID
exp: usize, // Expiration time (Unix timestamp)
iat: usize, // Issued at (Unix timestamp)
iss: String, // Issuer
pub sub: String,
pub exp: u64,
pub scope: Option<String>,
pub aud: Option<String>,
pub client_id: Option<String>,
pub iat: u64,
pub iss: Option<String>,
}

// Token revocation endpoint handler
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 5108f48

Please sign in to comment.