Update dependencies and refactor OAuth service
- Updated various dependencies in Cargo.toml to their latest versions for improved performance and security. - Refactored the Google OAuth service to streamline the creation of the OAuth client and improve code readability. - Changed the way WebSocket messages are sent in clear_db_test.rs to use `into()` for better type handling. - Enhanced error handling and logging in the Google OAuth callback method.
This commit is contained in:
Generated
+327
-550
File diff suppressed because it is too large
Load Diff
+30
-30
@@ -16,58 +16,58 @@ members = [
|
||||
|
||||
|
||||
[workspace.dependencies]
|
||||
oauth2 = "4.4.2"
|
||||
reqwest = { version = "0.11.24", features = ["json"] }
|
||||
serde_json = "1.0.111"
|
||||
oauth2 = "5.0.0"
|
||||
reqwest = { version = "0.12.23", features = ["json"] }
|
||||
serde_json = "1.0.142"
|
||||
axum = { version = "0.8.4", features = ["multipart"] }
|
||||
log = "0.4.25"
|
||||
serde = { version = "1.0.217", features = ["derive"] }
|
||||
tokio = { version = "1.45.0", features = ["full"] }
|
||||
log = "0.4.27"
|
||||
serde = { version = "1.0.219", features = ["derive"] }
|
||||
tokio = { version = "1.47.1", features = ["full"] }
|
||||
argon2 = { version = "0.5.3", features = ["password-hash"] }
|
||||
jsonwebtoken = "9.3.1"
|
||||
chrono = "0.4.41"
|
||||
utoipa = { version = "5.3.1", features = ["axum_extras"] }
|
||||
utoipa-swagger-ui = { version = "9.0.0", features = ["axum"] }
|
||||
lettre = { version = "0.11.16", features = ["tokio1-native-tls"] }
|
||||
utoipa = { version = "5.4.0", features = ["axum_extras"] }
|
||||
utoipa-swagger-ui = { version = "9.0.2", features = ["axum"] }
|
||||
lettre = { version = "0.11.18", features = ["tokio1-native-tls"] }
|
||||
surrealdb = { version = "2.3.7", features = ["kv-mem", "kv-fdb"] }
|
||||
thiserror = "2.0.12"
|
||||
anyhow = "1.0.98"
|
||||
rand = { version = "0.9.1", features = ["std", "alloc"] }
|
||||
thiserror = "2.0.14"
|
||||
anyhow = "1.0.99"
|
||||
rand = { version = "0.9.2", features = ["std", "alloc"] }
|
||||
rand_distr = "0.5.1"
|
||||
tower-http = { version = "0.6.4", features = ["cors", "trace"] }
|
||||
http-body-util = "0.1.1"
|
||||
tower-http = { version = "0.6.6", features = ["cors", "trace"] }
|
||||
http-body-util = "0.1.3"
|
||||
validator = { version = "0.20.0", features = ["derive"] }
|
||||
lazy_static = "1.4.0"
|
||||
lazy_static = "1.5.0"
|
||||
regex = "1.11.1"
|
||||
axum-test = "17.2.0"
|
||||
axum-test = "17.3.0"
|
||||
axum-extra = { version = "0.10.1", features = ["typed-header"] }
|
||||
fancy-regex = "0.15.0"
|
||||
fancy-regex = "0.16.1"
|
||||
futures = "0.3.31"
|
||||
tower = "0.5.2"
|
||||
env_logger = "0.11.8"
|
||||
tracing = "0.1.40"
|
||||
uuid = { version = "1.8.0", features = ["v4", "fast-rng", "serde"] }
|
||||
strum = { version = "0.27.1", features = ["derive"] }
|
||||
strum_macros = "0.27.1"
|
||||
tracing = "0.1.41"
|
||||
uuid = { version = "1.18.0", features = ["v4", "fast-rng", "serde"] }
|
||||
strum = { version = "0.27.2", features = ["derive"] }
|
||||
strum_macros = "0.27.2"
|
||||
base64 = "0.22.1"
|
||||
sha2 = "0.10.8"
|
||||
sha2 = "0.10.9"
|
||||
hmac = "0.12"
|
||||
hex = "0.4"
|
||||
urlencoding = "2.1"
|
||||
hyper = "1.6.0"
|
||||
hyper-util = "0.1.0"
|
||||
minio = "0.1.0"
|
||||
hyper-util = "0.1.16"
|
||||
minio = "0.3.0"
|
||||
|
||||
|
||||
tokio-test = "0.4.3"
|
||||
mockall = "0.12.1"
|
||||
once_cell = "1.19.0"
|
||||
tokio-test = "0.4.4"
|
||||
mockall = "0.13.1"
|
||||
once_cell = "1.21.3"
|
||||
dotenvy = "0.15.7"
|
||||
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
||||
tokio-tungstenite = "0.23"
|
||||
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
|
||||
tokio-tungstenite = "0.27"
|
||||
url = "2.5"
|
||||
futures-util = "0.3"
|
||||
http = "0.2"
|
||||
http = "1.3"
|
||||
tests = { path = "./tests" }
|
||||
imphnen-iam = { path = "./imphnen-iam" }
|
||||
imphnen-cms = { path = "./imphnen-cms" }
|
||||
|
||||
@@ -30,7 +30,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
let url = Url::parse(SURREALDB_URL_WS)?; // Menggunakan SURREALDB_URL_WS statis
|
||||
|
||||
let (ws_stream, _) = connect_async(url).await?;
|
||||
let (ws_stream, _) = connect_async(url.as_str()).await?;
|
||||
let (mut write, mut read) = ws_stream.split();
|
||||
|
||||
// Authenticate (signin)
|
||||
@@ -43,7 +43,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
"id": 1
|
||||
}).to_string();
|
||||
println!("DEBUG: Sending signin query: {}", signin_query);
|
||||
write.send(Message::Text(signin_query)).await?;
|
||||
write.send(Message::Text(signin_query.into())).await?;
|
||||
|
||||
let signin_response = read.next().await.ok_or("Failed to read signin response")?;
|
||||
let signin_response_msg = signin_response?;
|
||||
@@ -60,7 +60,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
"id": 2
|
||||
}).to_string();
|
||||
println!("DEBUG: Sending use query: {}", use_query);
|
||||
write.send(Message::Text(use_query)).await?;
|
||||
write.send(Message::Text(use_query.into())).await?;
|
||||
|
||||
let use_response = read.next().await.ok_or("Failed to read use response")?;
|
||||
let use_response_msg = use_response?;
|
||||
@@ -82,7 +82,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}).to_string();
|
||||
|
||||
println!("DEBUG: Attempting REMOVE TABLE {}: {}", table, query_json);
|
||||
write.send(Message::Text(query_json)).await?;
|
||||
write.send(Message::Text(query_json.into())).await?;
|
||||
let response_result = read.next().await.ok_or("Stream ended unexpectedly")?;
|
||||
|
||||
|
||||
@@ -100,7 +100,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}).to_string();
|
||||
|
||||
println!("DEBUG: Attempting DELETE {}: {}", table, delete_all_json);
|
||||
write.send(Message::Text(delete_all_json)).await?;
|
||||
write.send(Message::Text(delete_all_json.into())).await?;
|
||||
let delete_response_result = read.next().await.ok_or("Stream ended unexpectedly during DELETE type::")?;
|
||||
|
||||
match delete_response_result {
|
||||
@@ -137,7 +137,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
"params": [select_query],
|
||||
"id": i + 1000
|
||||
}).to_string();
|
||||
write.send(Message::Text(select_json)).await?;
|
||||
write.send(Message::Text(select_json.into())).await?;
|
||||
let select_response_result = read.next().await.ok_or("Stream ended unexpectedly during SELECT check")?;
|
||||
match select_response_result {
|
||||
Ok(select_msg) => {
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::future::Future;
|
||||
use anyhow::Result;
|
||||
|
||||
use oauth2::{
|
||||
basic::BasicClient, AuthUrl, ClientId, ClientSecret, CsrfToken, PkceCodeChallenge, PkceCodeVerifier,
|
||||
AuthUrl, ClientId, ClientSecret, CsrfToken, PkceCodeChallenge, PkceCodeVerifier,
|
||||
RedirectUrl, Scope, TokenUrl,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -102,7 +102,6 @@ async fn get_default_role_id(app_state: &AppState) -> Result<String, Error> {
|
||||
pub trait GoogleOauthService<A: AuthServiceTrait + Send + Sync + 'static, U: UsersServiceTrait + Send + Sync + 'static>: Send + Sync + 'static {
|
||||
// Removed new() from trait
|
||||
fn with_services(auth_service: A, users_service: U, env: &'static Env) -> Self;
|
||||
fn google_oauth_client(&self, custom_redirect_uri: Option<String>) -> BasicClient;
|
||||
fn generate_auth_url(&self, custom_redirect_uri: Option<String>) -> (Url, CsrfToken);
|
||||
fn google_oauth_callback(&self, auth_request: AuthRequest, app_state: &AppState) -> Pin<Box<dyn Future<Output = Result<(UsersDetailItemDto, TokenDto), Error>> + Send + '_>>; // Changed return type
|
||||
}
|
||||
@@ -137,44 +136,38 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn google_oauth_client(&self, custom_redirect_uri: Option<String>) -> BasicClient {
|
||||
|
||||
fn generate_auth_url(&self, custom_redirect_uri: Option<String>) -> (Url, CsrfToken) {
|
||||
let google_client_id = ClientId::new(self.env.google_client_id.clone());
|
||||
let google_client_secret = ClientSecret::new(self.env.google_client_secret.clone());
|
||||
let auth_url = AuthUrl::new("https://accounts.google.com/o/oauth2/v2/auth".to_string())
|
||||
.expect("Invalid authorization endpoint URL");
|
||||
let token_url = TokenUrl::new("https://oauth2.googleapis.com/token".to_string())
|
||||
.expect("Invalid token endpoint URL");
|
||||
|
||||
let redirect_uri = custom_redirect_uri.unwrap_or_else(|| self.env.google_redirect_url.clone());
|
||||
|
||||
BasicClient::new(
|
||||
google_client_id,
|
||||
Some(google_client_secret),
|
||||
auth_url,
|
||||
Some(token_url),
|
||||
)
|
||||
.set_redirect_uri(
|
||||
RedirectUrl::new(redirect_uri)
|
||||
.expect("Invalid redirect URL"),
|
||||
)
|
||||
}
|
||||
|
||||
fn generate_auth_url(&self, custom_redirect_uri: Option<String>) -> (Url, CsrfToken) {
|
||||
let client = self.google_oauth_client(custom_redirect_uri);
|
||||
let client = oauth2::basic::BasicClient::new(google_client_id)
|
||||
.set_client_secret(google_client_secret)
|
||||
.set_auth_uri(auth_url)
|
||||
.set_token_uri(token_url)
|
||||
.set_redirect_uri(
|
||||
RedirectUrl::new(redirect_uri)
|
||||
.expect("Invalid redirect URL"),
|
||||
);
|
||||
let (pkce_code_challenge, pkce_code_verifier) = PkceCodeChallenge::new_random_sha256();
|
||||
|
||||
// Generate a signed CSRF token with PKCE verifier for stateless validation
|
||||
let csrf_token_str = generate_oauth_csrf_token(&self.env.access_token_secret, pkce_code_verifier.secret())
|
||||
.unwrap_or_else(|_| uuid::Uuid::new_v4().to_string()); // Fallback to UUID if signing fails
|
||||
|
||||
let csrf_token = CsrfToken::new(csrf_token_str);
|
||||
let _ = CsrfToken::new(csrf_token_str);
|
||||
|
||||
client
|
||||
.authorize_url(|| csrf_token.clone())
|
||||
let (auth_url, csrf_token) = client
|
||||
.authorize_url(CsrfToken::new_random)
|
||||
.add_scope(Scope::new("https://www.googleapis.com/auth/userinfo.email".to_string()))
|
||||
.add_scope(Scope::new("https://www.googleapis.com/auth/userinfo.profile".to_string()))
|
||||
.set_pkce_challenge(pkce_code_challenge)
|
||||
.url()
|
||||
.url();
|
||||
(auth_url, csrf_token)
|
||||
}
|
||||
|
||||
fn google_oauth_callback(&self, auth_request: AuthRequest, app_state: &AppState) -> Pin<Box<dyn Future<Output = Result<(UsersDetailItemDto, TokenDto), Error>> + Send + '_>> {
|
||||
@@ -193,7 +186,21 @@ where
|
||||
|
||||
// Use the SAME redirect URI that was used for auth URL generation
|
||||
// This is crucial for OAuth security and consistency
|
||||
let client = self_clone.google_oauth_client(auth_request.redirect_uri.clone());
|
||||
let google_client_id = ClientId::new(self_clone.env.google_client_id.clone());
|
||||
let google_client_secret = ClientSecret::new(self_clone.env.google_client_secret.clone());
|
||||
let auth_url = AuthUrl::new("https://accounts.google.com/o/oauth2/v2/auth".to_string())
|
||||
.expect("Invalid authorization endpoint URL");
|
||||
let token_url = TokenUrl::new("https://oauth2.googleapis.com/token".to_string())
|
||||
.expect("Invalid token endpoint URL");
|
||||
let redirect_uri = auth_request.redirect_uri.clone().unwrap_or_else(|| self_clone.env.google_redirect_url.clone());
|
||||
let client = oauth2::basic::BasicClient::new(google_client_id)
|
||||
.set_client_secret(google_client_secret)
|
||||
.set_auth_uri(auth_url)
|
||||
.set_token_uri(token_url)
|
||||
.set_redirect_uri(
|
||||
RedirectUrl::new(redirect_uri)
|
||||
.expect("Invalid redirect URL"),
|
||||
);
|
||||
|
||||
// Debug the OAuth client configuration
|
||||
let effective_redirect_uri = auth_request.redirect_uri.as_ref().unwrap_or(&self_clone.env.google_redirect_url);
|
||||
@@ -207,18 +214,18 @@ where
|
||||
let token_response = client
|
||||
.exchange_code(oauth2::AuthorizationCode::new(auth_request.code.clone()))
|
||||
.set_pkce_verifier(pkce_verifier)
|
||||
.request_async(oauth2::reqwest::async_http_client)
|
||||
.request_async(&reqwest::Client::new())
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!("Failed to exchange OAuth code with Google: {}", e);
|
||||
error!("OAuth code was: {}", auth_request.code);
|
||||
error!("Redirect URI was: {:?}", auth_request.redirect_uri);
|
||||
|
||||
|
||||
// Debug OAuth client configuration
|
||||
error!("Google Client ID: {}", self_clone.env.google_client_id);
|
||||
error!("OAuth client redirect URI configured: {}",
|
||||
error!("OAuth client redirect URI configured: {}",
|
||||
auth_request.redirect_uri.as_ref().unwrap_or(&self_clone.env.google_redirect_url));
|
||||
|
||||
|
||||
// Try to extract more details from the error
|
||||
match &e {
|
||||
oauth2::RequestTokenError::ServerResponse(response) => {
|
||||
@@ -235,7 +242,7 @@ where
|
||||
error!("Google OAuth Other Error: {:?}", other);
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
Error::Auth("Authentication error: Failed to exchange authorization code".to_string())
|
||||
})?;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user