Browse Source

WIP: basic API

develop-api-fb
chodak166 4 months ago
parent
commit
ad13cf19f9
  1. 4
      config.toml
  2. 1
      lib/Cargo.toml
  3. 88
      lib/src/auth/infrastructure/jwt.rs

4
config.toml

@ -1,9 +1,9 @@
log_level = "trace"
[listen] [listen]
host = "0.0.0.0" host = "0.0.0.0"
port = 3000 port = 3000
log_level = "trace"
[auth] [auth]
firebase_project_id = "test-project" firebase_project_id = "test-project"
firebase_emulator_url = "http://192.168.1.23:9099" firebase_emulator_url = "http://192.168.1.23:9099"

1
lib/Cargo.toml

@ -21,6 +21,7 @@ sqlx = { version = "0.8.6", features = ["runtime-tokio", "sqlite", "chrono", "mi
futures = "0.3.31" futures = "0.3.31"
jsonwebtoken = "9.3" jsonwebtoken = "9.3"
reqwest = { version = "0.12", features = ["json"] } reqwest = { version = "0.12", features = ["json"] }
base64 = "0.22"
[dev-dependencies] [dev-dependencies]
mockall = "0.14.0" mockall = "0.14.0"

88
lib/src/auth/infrastructure/jwt.rs

@ -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);
tracing::debug!("Failed to decode header: {}", e.to_string());
AuthError::InvalidToken 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());
return Err(AuthError::InvalidToken);
}
};
let kid = header let kid = header
.kid .kid

Loading…
Cancel
Save