fix(manager-core): F-P-002 move Argon2id verify off the Tokio async worker
verify_password (Argon2id, OWASP defaults m=19456 KiB, t=2) is CPU-bound at tens-to-hundreds of ms and was invoked synchronously on the Tokio worker. Worse, verify_api_key Argon2-verifies every candidate sharing the 8-char prefix — a hot user with N keys serialized every admin request behind N×Argon2. Wrap each call site in tokio::task::spawn_blocking: - auth_middleware::verify_api_key (per request carrying a Bearer key) - auth_api::login (admin login) - users_service::login (data-plane app-user login, both real-hash and TIMING_FLAT_DUMMY_HASH branches) Cold-cache login is now ~2× current latency due to one spawn_blocking hop, but the worker no longer parks on Argon2 so steady-state under load is dramatically better. The LRU cache for the hot-path (token → principal) is finding F-P-009 — separate commit. AUDIT.md anchor: F-P-002. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -95,7 +95,17 @@ async fn login(State(state): State<AuthState>, Json(input): Json<LoginRequest>)
|
||||
None => (crate::auth::TIMING_FLAT_DUMMY_HASH.to_string(), None, false),
|
||||
};
|
||||
|
||||
let password_ok = verify_password(&stored_hash, &input.password);
|
||||
// F-P-002: Argon2id verify off the async worker.
|
||||
let password = input.password.clone();
|
||||
let password_ok = match tokio::task::spawn_blocking(move || verify_password(&stored_hash, &password))
|
||||
.await
|
||||
{
|
||||
Ok(b) => b,
|
||||
Err(err) => {
|
||||
tracing::error!(?err, "verify_password spawn_blocking join failed");
|
||||
return internal_error();
|
||||
}
|
||||
};
|
||||
if !password_ok || user_id.is_none() || !is_active {
|
||||
return invalid_credentials();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user