this repo has no description
1mod grants; 2mod helpers; 3mod introspect; 4mod types; 5 6use crate::oauth::OAuthError; 7use crate::state::{AppState, RateLimitKind}; 8use axum::body::Bytes; 9use axum::{Json, extract::State, http::HeaderMap}; 10 11pub use grants::{handle_authorization_code_grant, handle_refresh_token_grant}; 12pub use helpers::{TokenClaims, create_access_token, extract_token_claims, verify_pkce}; 13pub use introspect::{ 14 IntrospectRequest, IntrospectResponse, RevokeRequest, introspect_token, revoke_token, 15}; 16pub use types::{ 17 ClientAuthParams, GrantType, TokenGrant, TokenRequest, TokenResponse, ValidatedTokenRequest, 18}; 19 20fn extract_client_ip(headers: &HeaderMap) -> String { 21 if let Some(forwarded) = headers.get("x-forwarded-for") 22 && let Ok(value) = forwarded.to_str() 23 && let Some(first_ip) = value.split(',').next() 24 { 25 return first_ip.trim().to_string(); 26 } 27 if let Some(real_ip) = headers.get("x-real-ip") 28 && let Ok(value) = real_ip.to_str() 29 { 30 return value.trim().to_string(); 31 } 32 "unknown".to_string() 33} 34 35pub async fn token_endpoint( 36 State(state): State<AppState>, 37 headers: HeaderMap, 38 body: Bytes, 39) -> Result<(HeaderMap, Json<TokenResponse>), OAuthError> { 40 let content_type = headers 41 .get("content-type") 42 .and_then(|v| v.to_str().ok()) 43 .unwrap_or(""); 44 let request: TokenRequest = if content_type.starts_with("application/json") { 45 serde_json::from_slice(&body) 46 .map_err(|e| OAuthError::InvalidRequest(format!("Invalid JSON: {}", e)))? 47 } else if content_type.starts_with("application/x-www-form-urlencoded") { 48 serde_urlencoded::from_bytes(&body) 49 .map_err(|e| OAuthError::InvalidRequest(format!("Invalid form data: {}", e)))? 50 } else { 51 return Err(OAuthError::InvalidRequest( 52 "Content-Type must be application/json or application/x-www-form-urlencoded" 53 .to_string(), 54 )); 55 }; 56 let client_ip = extract_client_ip(&headers); 57 if !state 58 .check_rate_limit(RateLimitKind::OAuthToken, &client_ip) 59 .await 60 { 61 tracing::warn!(ip = %client_ip, "OAuth token rate limit exceeded"); 62 return Err(OAuthError::InvalidRequest( 63 "Too many requests. Please try again later.".to_string(), 64 )); 65 } 66 let dpop_proof = headers 67 .get("DPoP") 68 .and_then(|v| v.to_str().ok()) 69 .map(|s| s.to_string()); 70 let validated = request.validate()?; 71 match validated.grant { 72 TokenGrant::AuthorizationCode { .. } => { 73 handle_authorization_code_grant(state, headers, validated, dpop_proof).await 74 } 75 TokenGrant::RefreshToken { .. } => { 76 handle_refresh_token_grant(state, headers, validated, dpop_proof).await 77 } 78 } 79}