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::{TokenRequest, TokenResponse}; 17 18fn extract_client_ip(headers: &HeaderMap) -> String { 19 if let Some(forwarded) = headers.get("x-forwarded-for") 20 && let Ok(value) = forwarded.to_str() 21 && let Some(first_ip) = value.split(',').next() 22 { 23 return first_ip.trim().to_string(); 24 } 25 if let Some(real_ip) = headers.get("x-real-ip") 26 && let Ok(value) = real_ip.to_str() 27 { 28 return value.trim().to_string(); 29 } 30 "unknown".to_string() 31} 32 33pub async fn token_endpoint( 34 State(state): State<AppState>, 35 headers: HeaderMap, 36 body: Bytes, 37) -> Result<(HeaderMap, Json<TokenResponse>), OAuthError> { 38 let content_type = headers 39 .get("content-type") 40 .and_then(|v| v.to_str().ok()) 41 .unwrap_or(""); 42 let request: TokenRequest = if content_type.starts_with("application/json") { 43 serde_json::from_slice(&body) 44 .map_err(|e| OAuthError::InvalidRequest(format!("Invalid JSON: {}", e)))? 45 } else if content_type.starts_with("application/x-www-form-urlencoded") { 46 serde_urlencoded::from_bytes(&body) 47 .map_err(|e| OAuthError::InvalidRequest(format!("Invalid form data: {}", e)))? 48 } else { 49 return Err(OAuthError::InvalidRequest( 50 "Content-Type must be application/json or application/x-www-form-urlencoded" 51 .to_string(), 52 )); 53 }; 54 let client_ip = extract_client_ip(&headers); 55 if !state 56 .check_rate_limit(RateLimitKind::OAuthToken, &client_ip) 57 .await 58 { 59 tracing::warn!(ip = %client_ip, "OAuth token rate limit exceeded"); 60 return Err(OAuthError::InvalidRequest( 61 "Too many requests. Please try again later.".to_string(), 62 )); 63 } 64 let dpop_proof = headers 65 .get("DPoP") 66 .and_then(|v| v.to_str().ok()) 67 .map(|s| s.to_string()); 68 match request.grant_type.as_str() { 69 "authorization_code" => { 70 handle_authorization_code_grant(state, headers, request, dpop_proof).await 71 } 72 "refresh_token" => handle_refresh_token_grant(state, headers, request, dpop_proof).await, 73 _ => Err(OAuthError::UnsupportedGrantType(format!( 74 "Unsupported grant_type: {}", 75 request.grant_type 76 ))), 77 } 78}