|
|
|
@ -1,9 +1,8 @@ |
|
|
|
use crate::auth::domain::AuthClaims; |
|
|
|
use crate::auth::domain::AuthClaims; |
|
|
|
use crate::auth::traits::Authenticator; |
|
|
|
use crate::auth::traits::Authenticator; |
|
|
|
use crate::common::errors::AuthError; |
|
|
|
use crate::common::errors::AuthError; |
|
|
|
use jsonwebtoken::{ |
|
|
|
use base64::Engine; |
|
|
|
Algorithm, DecodingKey, Validation, dangerous_insecure_decode, decode, decode_header, |
|
|
|
use jsonwebtoken::{Algorithm, DecodingKey, Validation, decode, decode_header}; |
|
|
|
}; |
|
|
|
|
|
|
|
use serde::{Deserialize, Serialize}; |
|
|
|
use serde::{Deserialize, Serialize}; |
|
|
|
use std::collections::HashMap; |
|
|
|
use std::collections::HashMap; |
|
|
|
use std::sync::Arc; |
|
|
|
use std::sync::Arc; |
|
|
|
@ -90,6 +89,71 @@ impl JwtAuthenticator { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn decode_emulator_token(&self, token: &str) -> Result<AuthClaims, AuthError> { |
|
|
|
|
|
|
|
let parts: Vec<&str> = token.split('.').collect(); |
|
|
|
|
|
|
|
if parts.len() != 3 { |
|
|
|
|
|
|
|
return Err(AuthError::InvalidToken); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let header_json = base64::engine::general_purpose::URL_SAFE_NO_PAD |
|
|
|
|
|
|
|
.decode(parts[0]) |
|
|
|
|
|
|
|
.map_err(|e| { |
|
|
|
|
|
|
|
eprintln!("Failed to decode emulated header: {}", e); |
|
|
|
|
|
|
|
AuthError::InvalidToken |
|
|
|
|
|
|
|
})?; |
|
|
|
|
|
|
|
let header: serde_json::Value = |
|
|
|
|
|
|
|
serde_json::from_slice(&header_json).map_err(|_| AuthError::InvalidToken)?; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if header.get("alg").and_then(|v| v.as_str()) != Some("none") { |
|
|
|
|
|
|
|
return Err(AuthError::InvalidToken); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let payload_json = base64::engine::general_purpose::URL_SAFE_NO_PAD |
|
|
|
|
|
|
|
.decode(parts[1]) |
|
|
|
|
|
|
|
.map_err(|e| { |
|
|
|
|
|
|
|
eprintln!("Failed to decode emulated payload: {}", e); |
|
|
|
|
|
|
|
AuthError::InvalidToken |
|
|
|
|
|
|
|
})?; |
|
|
|
|
|
|
|
let claims: FirebaseClaims = |
|
|
|
|
|
|
|
serde_json::from_slice(&payload_json).map_err(|_| AuthError::InvalidToken)?; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let now = chrono::Utc::now().timestamp(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if claims.exp < now { |
|
|
|
|
|
|
|
return Err(AuthError::AuthenticationFailed("Token expired".to_string())); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if claims.aud != self.audience { |
|
|
|
|
|
|
|
return Err(AuthError::AuthenticationFailed( |
|
|
|
|
|
|
|
"Invalid audience".to_string(), |
|
|
|
|
|
|
|
)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if claims.iss != self.issuer { |
|
|
|
|
|
|
|
return Err(AuthError::AuthenticationFailed( |
|
|
|
|
|
|
|
"Invalid issuer".to_string(), |
|
|
|
|
|
|
|
)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let mut auth_claims = AuthClaims::new(claims.user_id) |
|
|
|
|
|
|
|
.with_expiration(claims.exp) |
|
|
|
|
|
|
|
.with_iat(claims.iat); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if let Some(email) = claims.email { |
|
|
|
|
|
|
|
auth_claims = auth_claims.with_email(email); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if let Some(ref firebase) = claims.firebase { |
|
|
|
|
|
|
|
if let Some(ref provider) = firebase.sign_in_provider { |
|
|
|
|
|
|
|
auth_claims = auth_claims.with_role(format!("auth:{}", provider)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
auth_claims = auth_claims.with_role("authenticated"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Ok(auth_claims) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
async fn get_public_keys(&self) -> Result<HashMap<String, DecodingKey>, AuthError> { |
|
|
|
async fn get_public_keys(&self) -> Result<HashMap<String, DecodingKey>, AuthError> { |
|
|
|
let cache = self.key_cache.read().await; |
|
|
|
let cache = self.key_cache.read().await; |
|
|
|
if let Some(ref cached) = *cache { |
|
|
|
if let Some(ref cached) = *cache { |
|
|
|
@ -160,10 +224,20 @@ impl Authenticator for JwtAuthenticator { |
|
|
|
)); |
|
|
|
)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
let header = decode_header(token).map_err(|e| { |
|
|
|
let header_result = decode_header(token); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let header = match header_result { |
|
|
|
|
|
|
|
Ok(h) => h, |
|
|
|
|
|
|
|
Err(e) => { |
|
|
|
|
|
|
|
if self.emulator_url.is_some() { |
|
|
|
|
|
|
|
if let Ok(claims) = self.decode_emulator_token(token) { |
|
|
|
|
|
|
|
return Ok(claims); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
tracing::debug!("Failed to decode header: {}", e.to_string()); |
|
|
|
tracing::debug!("Failed to decode header: {}", e.to_string()); |
|
|
|
AuthError::InvalidToken |
|
|
|
return Err(AuthError::InvalidToken); |
|
|
|
})?; |
|
|
|
} |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
let kid = header |
|
|
|
let kid = header |
|
|
|
.kid |
|
|
|
.kid |
|
|
|
|