fix(auth): bound store-time /auth/me validation so a slow backend defers instead of bouncing sign-in (#5166) - #5336
Conversation
…ers instead of bouncing sign-in (tinyhumansai#5166) The store-time GET /auth/me validation ran on the shared backend client's 120s request timeout, but the desktop sign-in RPC that drives auth_store_session gives up after 25s x 2 retries. A reachable-but-slow backend therefore let /auth/me hang past the frontend's patience: the RPC timed out and bounced a genuinely-authenticated user back to sign-in *before* the existing deferred-validation fallback could fire — the auth_me_timeout error in Sentry TAURI-REACT-1V. Cap store-time validation at a 12s budget (overridable via OPENHUMAN_AUTH_ME_STORE_TIMEOUT_MS), well under the frontend budget, and return a transient-classified timeout on exhaustion so a live-exp JWT routes into the caller-authorized pending-session path. The user lands in the app with deferred revalidation instead of being bounced.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughThe auth credential flow adds a configurable 12-second ChangesAuth validation timeout
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant SessionStore
participant CredentialOperations
participant AuthMeBackend
SessionStore->>CredentialOperations: Validate session user
CredentialOperations->>AuthMeBackend: Request /auth/me
AuthMeBackend-->>CredentialOperations: Response or timeout
CredentialOperations-->>SessionStore: Identity or deferred validation
Suggested labels: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/openhuman/security/credentials/ops_tests.rs`:
- Around line 376-386: In the timeout assertion for
store_session_with_deferred_validation, replace the 10-second upper bound with
one safely above the configured 200ms budget but below the default 12-second
budget, so multi-second regressions fail while allowing normal timing variance.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: bdd7676f-6386-40e6-a4b3-89110a4a440c
📒 Files selected for processing (3)
.env.examplesrc/openhuman/security/credentials/ops.rssrc/openhuman/security/credentials/ops_tests.rs
|
| Filename | Overview |
|---|---|
| src/openhuman/security/credentials/ops.rs | Adds AUTH_ME_STORE_VALIDATION_BUDGET constant and auth_me_store_validation_budget() resolver, wraps the store-time /auth/me call in tokio::time::timeout, renames original body to _inner. Transient error message correctly contains 'timeout' to satisfy the existing auth_me_store_failure_is_transient classifier. No correctness issues found. |
| src/openhuman/security/credentials/ops_tests.rs | Adds EnvVarGuard::set, spawn_auth_me_hang, and two new tests exercising the timeout path and the budget-resolver env-var branches. Timing assertion has 10x headroom (200 ms budget vs 2 s wall-clock bound), which is reasonable for CI. |
| .env.example | Adds commented documentation for OPENHUMAN_AUTH_ME_STORE_TIMEOUT_MS with the correct default (12000) and context. No issues. |
Sequence Diagram
sequenceDiagram
participant FE as Frontend (desktopDeepLinkListener)
participant RPC as store_session_inner
participant T as tokio::time::timeout (12 s budget)
participant Inner as fetch_current_user_inner
participant BE as Backend GET /auth/me
FE->>RPC: auth_store_session (25 s RPC timeout)
RPC->>T: start 12 s budget
T->>Inner: call
Inner->>BE: GET /auth/me (first attempt)
alt "Backend healthy (< 12 s)"
BE-->>Inner: 200 OK + user JSON
Inner-->>T: Ok(user)
T-->>RPC: Ok(user)
RPC-->>FE: session stored
else "Backend slow (> 12 s) — NEW PATH"
Note over T: 12 s elapsed
T-->>RPC: Err(timeout)
Note over RPC: auth_me_store_failure_is_transient = true, allow_pending_backend_validation = true, jwt_exp_live_at = Some(exp)
RPC-->>FE: session stored (pendingBackendValidation: true)
else Non-transient failure (401)
BE-->>Inner: 401 Unauthorized
Inner-->>T: Err(401)
T-->>RPC: Err(401)
Note over RPC: not transient, hard fail
RPC-->>FE: Err (user bounces to sign-in)
end
Reviews (2): Last reviewed commit: "test(auth): tighten store-time budget as..." | Re-trigger Greptile
…5166) CodeRabbit: the <10s upper bound let a multi-second timeout regression pass. With a 200ms configured budget and 12s default, bound at <2s so a regression fails while allowing normal timing variance.
Summary
GET /auth/mesign-in validation to a 12s budget (was the shared backend client's 120s request ceiling), well under the desktop sign-in RPC timeout.expJWT routes into the existing caller-authorized deferred-validation path instead of hanging.OPENHUMAN_AUTH_ME_STORE_TIMEOUT_MS(documented in.env.example).Problem
Sentry
TAURI-REACT-1V—Error: auth store failed: auth_me_timeout(26 events, 4 users, production).Tracing the invariant "a valid, OAuth-succeeded session must not bounce back to sign-in" backward:
GET /auth/mevalidation infetch_current_user_for_session_store(src/openhuman/security/credentials/ops.rs) runs on the sharedBackendOAuthClient's 120s request timeout (src/api/rest.rs:279-280).auth_store_sessiongives up far sooner —AUTH_STORE_TIMEOUT_MS(25s) ×AUTH_STORE_RETRIES(2) inapp/src/utils/desktopDeepLinkListener.ts./auth/mehang past the frontend's patience: the RPC times out and bounces the user before the existingallowPendingBackendValidationdeferred-validation fallback (ops.rs, which persists a live-expJWT for later revalidation) ever gets a chance to fire.PR #5171 already downgraded the Sentry level to
warningfor transient kinds — that reduced noise but did not close the user-facing bounce, because the fast-fail-into-deferred path still couldn't trigger under a slow backend.Solution
/auth/mecall intokio::time::timeout(auth_me_store_validation_budget()); the prior body becomesfetch_current_user_for_session_store_inner."GET /auth/me validation timeout after {ms}ms (store-time budget exceeded)"— worded to contain aTRANSIENT_TRANSPORT_PHRASESphrase ("timeout") so the existingauth_me_store_failure_is_transientclassifier buckets it as transient andstore_session_innerroutes a live-expJWT into the deferred-validation branch.AUTH_ME_STORE_VALIDATION_BUDGET), comfortably under the frontend's 25s outer bound; overridable viaOPENHUMAN_AUTH_ME_STORE_TIMEOUT_MSfor ops tuning/tests. Invalid/non-positive overrides fall back to the default.Design note: the fallback still requires a locally-valid JWT (
jwt_exp_live_at) and stores only{ pendingBackendValidation: true }— it does not copy identity claims from an unverified token, so this does not weaken the auth contract. A genuine401/non-transient failure is unaffected and still hard-fails to sign-in.Submission Checklist
pnpm test:rustlocally); the timeoutErrbranch, budget resolver, and warn path are all covered## Related— N/A (behaviour-only)Closes #5166in## RelatedImpact
/auth/meresponds well under 1s).expJWT and never copies identity claims; non-transient (401) failures still hard-fail.Related
AI Authored PR Metadata (required for Codex/Linear PRs)
Linear Issue
Commit & Branch
fix/auth-me-store-timeout-5166Validation Run
pnpm --filter openhuman-app format:check— N/A (noapp/srcchanges)pnpm typecheck— N/A (no TS changes)cargo test --lib security::credentials::ops— 50 passedcargo fmt --checkclean,cargo clippy --libcleanValidation Blocked
command:N/Aerror:N/Aimpact:N/ABehavior Changes
/auth/mefails fast into deferred validation instead of hanging until the sign-in RPC bounces the user.Parity Contract
401/non-transient failures, and non-live-exptokens behave exactly as before.auth_me_store_failure_is_transientclassification and thejwt_exp_live_atgate are unchanged; only the trigger (timeout vs. upstream error) is new.Duplicate / Superseded PR Handling
Summary by CodeRabbit
New Features
OPENHUMAN_AUTH_ME_STORE_TIMEOUT_MS.Bug Fixes
Documentation