fix: address Phase 1-2 audit findings

- CORS: replace permissive() with configurable whitelist (default.toml)
- Auth store: synchronously restore state at creation to eliminate
  flash-of-login-page on refresh
- MainLayout: menu highlight now tracks current route via useLocation
- Add extractErrorMessage() utility to reduce repeated error parsing
- Fix all clippy warnings across 4 crates (erp-auth, erp-config,
  erp-workflow, erp-message): remove unnecessary casts, use div_ceil,
  collapse nested ifs, reduce function arguments with DTOs
This commit is contained in:
iven
2026-04-11 12:36:34 +08:00
parent 5c899e6f4a
commit 3a05523d23
35 changed files with 283 additions and 187 deletions

View File

@@ -12,6 +12,13 @@ use crate::error::AuthResult;
use super::password;
use super::token_service::TokenService;
/// JWT configuration needed for token signing.
pub struct JwtConfig<'a> {
pub secret: &'a str,
pub access_ttl_secs: i64,
pub refresh_ttl_secs: i64,
}
/// Authentication service handling login, token refresh, and logout.
pub struct AuthService;
@@ -32,9 +39,7 @@ impl AuthService {
username: &str,
password_plain: &str,
db: &sea_orm::DatabaseConnection,
jwt_secret: &str,
access_ttl_secs: i64,
refresh_ttl_secs: i64,
jwt: &JwtConfig<'_>,
event_bus: &EventBus,
) -> AuthResult<LoginResp> {
// 1. Find user by tenant_id + username
@@ -85,15 +90,15 @@ impl AuthService {
tenant_id,
roles.clone(),
permissions,
jwt_secret,
access_ttl_secs,
jwt.secret,
jwt.access_ttl_secs,
)?;
let (refresh_token, _) = TokenService::sign_refresh_token(
user_model.id,
tenant_id,
db,
jwt_secret,
refresh_ttl_secs,
jwt.secret,
jwt.refresh_ttl_secs,
)
.await?;
@@ -129,7 +134,7 @@ impl AuthService {
Ok(LoginResp {
access_token,
refresh_token,
expires_in: access_ttl_secs as u64,
expires_in: jwt.access_ttl_secs as u64,
user: user_resp,
})
}
@@ -138,13 +143,11 @@ impl AuthService {
pub async fn refresh(
refresh_token_str: &str,
db: &sea_orm::DatabaseConnection,
jwt_secret: &str,
access_ttl_secs: i64,
refresh_ttl_secs: i64,
jwt: &JwtConfig<'_>,
) -> AuthResult<LoginResp> {
// Validate existing refresh token
let (old_token_id, claims) =
TokenService::validate_refresh_token(refresh_token_str, db, jwt_secret).await?;
TokenService::validate_refresh_token(refresh_token_str, db, jwt.secret).await?;
// Revoke the old token (rotation)
TokenService::revoke_token(old_token_id, db).await?;
@@ -161,15 +164,15 @@ impl AuthService {
claims.tid,
roles.clone(),
permissions,
jwt_secret,
access_ttl_secs,
jwt.secret,
jwt.access_ttl_secs,
)?;
let (new_refresh_token, _) = TokenService::sign_refresh_token(
claims.sub,
claims.tid,
db,
jwt_secret,
refresh_ttl_secs,
jwt.secret,
jwt.refresh_ttl_secs,
)
.await?;
@@ -195,7 +198,7 @@ impl AuthService {
Ok(LoginResp {
access_token,
refresh_token: new_refresh_token,
expires_in: access_ttl_secs as u64,
expires_in: jwt.access_ttl_secs as u64,
user: user_resp,
})
}