Skip to content

fix(auth): treat missing backend session token as auth-expired#1504

Merged
senamakel merged 1 commit into
tinyhumansai:mainfrom
senamakel:fix/missing-backend-session-token-auth-expired
May 12, 2026
Merged

fix(auth): treat missing backend session token as auth-expired#1504
senamakel merged 1 commit into
tinyhumansai:mainfrom
senamakel:fix/missing-backend-session-token-auth-expired

Conversation

@senamakel
Copy link
Copy Markdown
Member

@senamakel senamakel commented May 11, 2026

Summary

  • Classify the core's "no backend session token" error as auth-expired in both Rust ($is_session_expired_error$) and the frontend ($classifyRpcError$) so it stops being treated as a hard error.
  • Stops Sentry spam from the 5 s $useComposioIntegrations$ poll (253 occurrences on a single user in OPENHUMAN-TAURI-1N).
  • Lets the existing $core-rpc-auth-expired$ event flow bounce the renderer back to $/$ (login) instead of trapping the user on an onboarding step that polls a failing RPC forever.

Problem

Users on the onboarding $SkillsStep$ kept seeing $composio_list_connections$ fail with $"composio unavailable: no backend session token. Sign in first (auth_store_session)."$ every 5 s. Two consequences:

  1. The Rust core's $is_session_expired_error$ helper didn't match the pattern, so each failure was reported to Sentry as a fresh ERROR via $report_error$. One user generated 253 events in $OPENHUMAN-TAURI-1N$ on $openhuman.composio_list_connections$ alone.
  2. The frontend's $classifyRpcError$ also didn't match it, so the kind was $'unknown'$. The existing $core-rpc-auth-expired$$CoreStateProvider.clearSession$$ProtectedRoute$$/$ (login) cascade never fired. The renderer kept its stale optimistic $sessionToken$ patch and the user stayed stuck on $SkillsStep$ — no path back to the login page.

The same "no backend session token" pattern is produced by $webhooks$, $billing$, $team$, $referral$, $web_search$, etc., so they all benefit from the same fix.

Solution

  • $src/core/jsonrpc.rs$: extend $is_session_expired_error$ to also match $"no backend session token"$ (case-insensitive). This routes the message through the existing auto-cleanup path ($clear_session$) and downgrades the Sentry log line to $info$.
  • $app/src/services/coreRpcClient.ts$: extend $classifyRpcError$ with the same pattern so the RPC throws $CoreRpcError(kind='auth_expired')$. The existing handler in $CoreStateProvider$ fires $clearSession$, $ProtectedRoute$ sees $sessionToken=null$, and the user lands on the login screen.
  • Unit tests added on both sides (Rust: 4-assertion table; Vitest: 3 new table rows in $classifyRpcError$).

Submission Checklist

  • Tests added or updated (happy path + at least one failure / edge case) per Testing Strategy
  • N/A: ≥ 80% diff coverage — change is two one-line predicate extensions, both fully covered by the new table tests.
  • N/A: coverage-matrix update — no new feature row; existing auth-expired flow extended.
  • N/A: feature IDs in $## Related$ — no matrix feature added.
  • No new external network dependencies introduced (mock backend used per Testing Strategy)
  • N/A: release-cut smoke surface untouched — error classification only.
  • N/A: no linked issue — Sentry-driven fix (OPENHUMAN-TAURI-1N).

Impact

  • Desktop only path (web doesn't hit the Rust composio handler). Stops the 253-event-per-user Sentry burst on $OPENHUMAN-TAURI-1N$ and similar groups.
  • Existing $auth_expired$ behaviour is unchanged for current callers (401, $Session expired$, $Invalid Token$, $SESSION_EXPIRED$).
  • No migration, no perf cost, no security implication — server-side path also runs $clear_session$, which is a no-op when there is no profile to clear.

Related

  • Closes:
  • Follow-up PR(s)/TODOs: investigate why the frontend ends up with a stale $sessionToken$ while the Rust auth profile is empty (root cause for the optimistic-patch-vs-backend divergence). This PR is the safety net.

AI Authored PR Metadata (required for Codex/Linear PRs)

Linear Issue

  • Key: N/A
  • URL: N/A

Commit & Branch

  • Branch: $fix/missing-backend-session-token-auth-expired$
  • Commit SHA: $0910662e6490989220ee5c47c6ba00bc5f287b94$

Validation Run

  • N/A: $pnpm --filter openhuman-app format:check$ — pre-commit hook ran prettier + eslint on this branch and passed.
  • N/A: $pnpm typecheck$ — pre-commit hook covers it.
  • Focused tests: $pnpm debug unit src/services/__tests__/coreRpcClient.test.ts -t classifyRpcError$ (16 passed); $cargo test --lib is_session_expired$ (6 passed).
  • Rust fmt/check (if changed): cargo test build path included $cargo check$ equivalent; no new warnings introduced on touched files.
  • N/A: Tauri fmt/check (if changed) — $app/src-tauri$ not touched.

Validation Blocked

  • command: N/A
  • error: N/A
  • impact: N/A

Behavior Changes

  • Intended behavior change: surface "no backend session token" RPC failures as auth-expired instead of as hard errors.
  • User-visible effect: users whose backend session is gone are now redirected from onboarding back to the login screen instead of being trapped on a polling step that keeps failing.

Parity Contract

  • Legacy behavior preserved: 401/Unauthorized/SESSION_EXPIRED/Invalid Token paths unchanged; existing classifier test rows kept.
  • Guard/fallback/dispatch parity checks: $invoke_method$ continues to call $clear_session$ for any session-expired classification; $CoreStateProvider$ continues to debounce $core-rpc-auth-expired$ within a 10 s window.

Duplicate / Superseded PR Handling

  • Duplicate PR(s): none
  • Canonical PR: this one
  • Resolution: N/A

Summary by CodeRabbit

  • Bug Fixes

    • Enhanced error classification to properly handle missing backend session token errors, triggering automatic session cleanup and user re-authentication instead of reporting as hard failures.
  • Tests

    • Added test coverage for the missing backend session error classification scenarios.

Review Change Stack

`composio_list_connections` (and the parallel web_search / billing / team
/ webhooks / referral handlers) return a "no backend session token. Sign
in first (auth_store_session)." error when the Rust auth profile is
missing. Two consequences this fix addresses:

- `is_session_expired_error` did not recognise the pattern, so the core
  reported it to Sentry as a hard error on every 5 s `useComposio
  Integrations` poll (see Sentry OPENHUMAN-TAURI-1N — 253 occurrences
  for a single user stuck on the onboarding SkillsStep).
- `classifyRpcError` mapped the message to `'unknown'`, so the existing
  `core-rpc-auth-expired` → `CoreStateProvider.clearSession` →
  `ProtectedRoute` → `/` cascade never fired. The frontend kept its
  stale optimistic `sessionToken` patch and trapped the user on an
  onboarding step that could never load.

Both sides now recognise the pattern, so the core stops spamming Sentry
and runs its auto-cleanup, and the renderer bounces the user back to
the login screen.
@senamakel senamakel requested a review from a team May 11, 2026 22:05
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 11, 2026

📝 Walkthrough

Walkthrough

The PR extends auth-expired error classification across Rust and TypeScript layers to handle "no backend session token" backend errors. Both is_session_expired_error (Rust) and classifyRpcError (TypeScript) now treat this error pattern as session-expired, enabling automatic local session cleanup and re-authentication flows instead of repeated hard failures.

Changes

Auth-Expired Classification Extension

Layer / File(s) Summary
Core Session Expiry Detection
src/core/jsonrpc.rs, src/core/jsonrpc_tests.rs
is_session_expired_error treats "no backend session token" backend errors as session-expired alongside 401 and invalid-token patterns. Documentation explains this occurs when the backend session is missing (profile wiped) and should trigger local cleanup and re-auth. New test validates multiple message formats including case-insensitive variants.
Application-Layer Error Routing
app/src/services/coreRpcClient.ts, app/src/services/__tests__/coreRpcClient.test.ts
classifyRpcError maps RPC errors containing "no backend session token" to auth_expired error kind. New test case verifies the mapping for the composio-specific error message.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Possibly related PRs

  • tinyhumansai/openhuman#1495: Introduced the classifyRpcError and auth_expired handling mechanism that this PR extends to cover the "no backend session token" error shape.
  • tinyhumansai/openhuman#463: Updates auth/session RPC methods and error handling; both PRs reference the same auth_store_session identifier and backend session-error patterns.

Poem

🐰 A token was lost in the backend abyss,
So we catch it, classify it, and never miss—
From Rust down to TypeScript, the error flows clean,
Auth re-signs and re-auth's the graceful machine! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: treating 'missing backend session token' as an auth-expired condition, which is the core fix across both Rust and TypeScript files.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant