Skip to content

fix: resolve session expiry by implementing automatic token refresh (#14)#88

Open
Arjita-2B-18 wants to merge 3 commits into
jpdevhub:mainfrom
Arjita-2B-18:main
Open

fix: resolve session expiry by implementing automatic token refresh (#14)#88
Arjita-2B-18 wants to merge 3 commits into
jpdevhub:mainfrom
Arjita-2B-18:main

Conversation

@Arjita-2B-18

@Arjita-2B-18 Arjita-2B-18 commented Jun 9, 2026

Copy link
Copy Markdown

Summary

Fixes #14 — users were being logged out after 1 hour due to missing refresh token logic.

Changes

  • backend/auth.py — added POST /api/v1/auth/refresh endpoint
  • backend/main.py — registered auth router
  • src/pages/AuthPage.tsx — now saves refresh_token to localStorage on login
  • src/lib/api.tssafeFetch now intercepts 401, refreshes token silently, retries request. On failure clears tokens and redirects to /auth

Behavior

Users stay logged in across long sessions. Token refresh is fully silent with no UI interruption.

Summary by CodeRabbit

  • New Features

    • Server-backed token refresh support to transparently renew sessions.
  • Enhancements

    • Client will persist refresh tokens from auth callbacks for smoother re-authentication.
    • Failed requests due to expired tokens are retried once after refresh; on failure tokens are cleared and users are sent to sign-in.
  • Bug Fixes

    • Improved error handling for invalid/expired refresh attempts.
  • Chores

    • Development dependency updates.

@vercel

vercel Bot commented Jun 9, 2026

Copy link
Copy Markdown

@Arjita-2B-18 is attempting to deploy a commit to the karan3431's projects Team on Vercel.

A member of the Team first needs to authorize it.

@github-actions

github-actions Bot commented Jun 9, 2026

Copy link
Copy Markdown

🎉 Thank you for your Pull Request! We're thrilled to have your contribution to FreshScan AI.

Before we review, please make sure you have:

  • Followed the CONTRIBUTING.md guidelines.
  • Ensured all automated CI checks (linting, tests) are passing.
  • Checked that your commit messages follow the Conventional Commits format.

A maintainer will review your code as soon as possible!

@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

Pull request was closed or merged during review

Warning

.coderabbit.yaml has a parsing error

The CodeRabbit configuration file in this repository has a parsing error and default settings were used instead. Please fix the error(s) in the configuration file. You can initialize chat with CodeRabbit to get help with the configuration file.

💥 Parsing errors (1)
Validation error: Invalid input: expected object, received boolean at "reviews.auto_review"
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
📝 Walkthrough

Walkthrough

The PR implements automatic session refresh: backend adds POST /api/v1/auth/refresh to exchange refresh tokens for new sessions; frontend saves refresh_token from the OAuth callback and uses it to recover from 401s by calling the refresh endpoint and retrying the original request once before redirecting to login on failure.

Changes

Session Refresh Flow

Layer / File(s) Summary
Backend refresh endpoint and wiring
backend/auth.py, backend/main.py
New POST /api/v1/auth/refresh endpoint accepts a refresh_token and calls Supabase's auth.refresh_session() to obtain new tokens; returns tokens on success or raises HTTPException(401, ...) on invalid/expired tokens or other errors. Router is imported and registered with the FastAPI app after initialization.
Frontend refresh token capture and storage
src/pages/AuthPage.tsx
OAuth callback handler extracts refresh_token from URL query parameters and persists it to localStorage under key fs_refresh_token before proceeding with the existing redirect flow.
Frontend token refresh and request retry
src/lib/api.ts
Adds REFRESH_KEY, extends clearToken() to also remove the refresh token, implements tryRefreshToken() to POST the stored refresh token to the backend and persist returned tokens, centralizes handleResponse (including 5xx handling), and updates safeFetch() to attempt a one-time refresh on 401 and retry the original request; api.submitScan no longer accepts meta.

Dependency / Merge Conflict

Layer / File(s) Summary
package.json devDependencies and conflict markers
package.json
Bumps @types/node, updates @vitejs/plugin-react and vite versions and leaves unresolved merge-conflict markers around the vite/PWA dependency block (conflicting presence of vite-plugin-pwa/workbox-window).

Sequence Diagram(s)

sequenceDiagram
  participant Browser
  participant Frontend
  participant BackendRefreshEndpoint
  participant SupabaseAuth
  Browser->>Frontend: API request with access_token
  Frontend->>BackendRefreshEndpoint: API request (expired) -> 401
  Frontend->>BackendRefreshEndpoint: POST /api/v1/auth/refresh (fs_refresh_token)
  BackendRefreshEndpoint->>SupabaseAuth: auth.refresh_session(refresh_token)
  SupabaseAuth->>BackendRefreshEndpoint: new access_token + refresh_token
  BackendRefreshEndpoint->>Frontend: {access_token, refresh_token}
  Frontend->>Frontend: persist tokens, retry original request with new access_token
  Frontend->>BackendRefreshEndpoint: retried API request (new access_token)
  BackendRefreshEndpoint->>Frontend: success response
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

Hard, size/level: hard

Suggested reviewers

  • jpdevhub

Poem

🐰 I found a token, worn and thin,
I hop and swap to let you in.
One POST, one store, a retry spun,
A fresh new key — the session's won.
Hooray, stay logged in — hop on and run!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR contains unrelated changes to package.json with merge conflict markers and dependency version updates unrelated to token refresh functionality. Resolve the merge conflict in package.json (<<<<<<< HEAD / ======= / >>>>>>>) and remove unrelated dependency updates, or justify these changes in the PR description.
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: implementing automatic token refresh to fix session expiry.
Linked Issues check ✅ Passed All core requirements from issue #14 are addressed: backend refresh endpoint, frontend token persistence, 401 interception with refresh, and token cleanup on failure.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Biome (2.4.16)
package.json

File contains syntax errors that prevent linting: Line 50: unexpected character <; Line 50: unexpected character <; Line 50: unexpected character <; Line 50: unexpected character <; Line 50: unexpected character <; Line 50: unexpected character <; Line 50: unexpected character <; Line 50: String values must be double quoted.; Line 51: End of file expected; Line 51: End of file expected; Line 51: End of file expected; Line 51: End of file expected; Line 52: End of file expected; Line 52: End of file expected; Line 52: End of file expected; Line 52: End of file expected; Line 53: End of file expected; Line 53: End of file expected; Line 53: End of file expected; Line 54: unexpected character =; Line 54: unexpected character =; Line 54: unexpected character =; Line 54: unexpected character =; Line 54: unexpected character =; Line 54: unexpected character =; Line 54: unexpected character =; Line 55: End of file expected; Line 55: End of file expected; Line 55: End of file expected; Line 56: unexpected character >; Line 56: unexpected character >; Line 56: unexpected character >; Line 56: unexpected character >; Line 56: unexpected character >; Line 56: unexpected character >; Line 56: unexpected character >; Line 56: End of file expected; Line 56: String values must be double quoted.; Line 56: unexpected character (; Line 56: String values must be double quoted.; Line 56: End of file expected; Line 56: unexpected character .; Line 56: unexpected character )

src/lib/api.ts

File contains syntax errors that prevent linting: Line 62: Expected a statement but instead found '<<<<<<< HEAD'.; Line 80: Expected a statement but instead found '=======

8f270f1 (fix: resolve all TypeScript errors in api.ts)'.; Line 81: numbers cannot be followed by identifiers directly after; Line 132: Expected a statement but instead found '<<<<<<< HEAD'.; Line 135: Illegal use of an export declaration not at the top level; Line 136: Expected a statement but instead found '======='.; Line 137: Illegal use of an export declaration not at the top level; Line 138: Expected a statement but instead found '>>>>>>> 8f270f1 (fix: resolve all TypeScript errors in api.ts)'.; Line 138: numbers cannot be followed by identifiers directly after; Line 139: Illegal use of an export declaration not at the top level; Line 140: Illegal use of an export declaration not at the top level; Line 141: Illegal use of an export declaration not at the top level; Line 143: Expected a statement but inst

... [truncated 1289 characters] ...

; Line 214: Expected a statement but instead found ',

getLatestScan: (): Promise =>
apiFetch('/ap; Line 223: expected : but instead found {; Line 223: Expected a semicolon or an implicit semicolon after a statement, but found none; Line 228: Expected a semicolon or an implicit semicolon after a statement, but found none; Line 230: expected : but instead found {; Line 230: Expected a semicolon or an implicit semicolon after a statement, but found none; Line 235: Expected a semicolon or an implicit semicolon after a statement, but found none; Line 235: Expected a semicolon or an implicit semicolon after a statement, but found none; Line 235: Invalid regex flag; Line 235: unterminated template literal; Line 255: expected } but instead the file ends


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
backend/auth.py (1)

25-28: ⚡ Quick win

Reuse the existing Supabase client singleton instead of creating a new client per request.

The file already defines a get_auth_client() singleton (lines 77-81) that should be reused here. Creating a new client on every refresh request adds unnecessary overhead.

♻️ Proposed fix
+from auth import get_auth_client
+
 `@router.post`("/refresh")
 async def refresh_session(body: RefreshRequest):
     """
     Exchange a Supabase refresh_token for a new access_token + refresh_token.
     Called automatically by the frontend when a 401 Unauthorized error occurs.
     """
     try:
-        # Create a Supabase client using environment variables
-        supabase = create_client(
-            os.environ["SUPABASE_URL"],
-            os.environ["SUPABASE_KEY"]
-        )
+        # Use the existing singleton client
+        supabase = get_auth_client()

Note: Since get_auth_client() is defined later in the same file, you may need to reorganize the file to place the router code after the helper functions, or extract the client singleton to a shared module.

🤖 Prompt for 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.

In `@backend/auth.py` around lines 25 - 28, Replace the per-request Supabase
client creation in the refresh logic with the existing singleton accessor
get_auth_client(): stop calling create_client(...) inside the request handler
(the variable currently named supabase) and instead call get_auth_client() to
obtain the shared client; if get_auth_client() is defined after the router,
either move the router below the helper or move the singleton into a common
module so the handler can import and reuse it. Ensure all references that used
the local supabase variable now use the returned client from get_auth_client().
src/pages/AuthPage.tsx (1)

34-36: 💤 Low value

Consider importing and using the shared constant for the refresh token key.

The key 'fs_refresh_token' is hardcoded here but defined as REFRESH_KEY in api.ts. Using the shared constant would prevent key mismatches if renamed later.

♻️ Proposed fix

In api.ts, export the constant:

export const REFRESH_KEY = 'fs_refresh_token';

Then in AuthPage.tsx:

-import { api, setToken, isAuthenticated } from '../lib/api';
+import { api, setToken, isAuthenticated, REFRESH_KEY } from '../lib/api';

 if (refreshToken) {
-  localStorage.setItem('fs_refresh_token', refreshToken);
+  localStorage.setItem(REFRESH_KEY, refreshToken);
 }
🤖 Prompt for 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.

In `@src/pages/AuthPage.tsx` around lines 34 - 36, Replace the hardcoded
'fs_refresh_token' string in the AuthPage logic with the exported shared
constant REFRESH_KEY from api.ts: export REFRESH_KEY from api.ts (if not already
exported) and import REFRESH_KEY into AuthPage.tsx, then use REFRESH_KEY in the
localStorage.setItem call inside the AuthPage component (the block that
currently checks refreshToken and calls localStorage.setItem).
🤖 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 `@backend/auth.py`:
- Around line 46-48: The except block that currently does "except Exception as
e: raise HTTPException(status_code=401, detail=f'Token refresh failed:
{str(e)}')" should be changed to avoid leaking internal error text: replace the
HTTPException detail with a generic message like "Token refresh failed" or
"Unauthorized", and log the original exception internally using an application
logger (e.g., logger.exception or logger.error with the exception) before
raising HTTPException; keep the status_code=401 and continue to raise
HTTPException but do not include str(e) in the response.

In `@src/lib/api.ts`:
- Around line 69-82: The 401 branch in handleResponse is redundant and broken:
remove the entire "if (res.status === 401 && !retried) { ... }" block from
handleResponse so it no longer tries to refresh tokens or return the original
failed Response; instead let safeFetch continue to own 401 handling and retries
(keep tryRefreshToken/clearToken usage only where safeFetch implements them).
Ensure handleResponse only processes non-OK responses generically and that
callers still call safeFetch which handles the retry flow.

---

Nitpick comments:
In `@backend/auth.py`:
- Around line 25-28: Replace the per-request Supabase client creation in the
refresh logic with the existing singleton accessor get_auth_client(): stop
calling create_client(...) inside the request handler (the variable currently
named supabase) and instead call get_auth_client() to obtain the shared client;
if get_auth_client() is defined after the router, either move the router below
the helper or move the singleton into a common module so the handler can import
and reuse it. Ensure all references that used the local supabase variable now
use the returned client from get_auth_client().

In `@src/pages/AuthPage.tsx`:
- Around line 34-36: Replace the hardcoded 'fs_refresh_token' string in the
AuthPage logic with the exported shared constant REFRESH_KEY from api.ts: export
REFRESH_KEY from api.ts (if not already exported) and import REFRESH_KEY into
AuthPage.tsx, then use REFRESH_KEY in the localStorage.setItem call inside the
AuthPage component (the block that currently checks refreshToken and calls
localStorage.setItem).
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 206f103c-4313-4cb1-bc34-ad2ddbe884f9

📥 Commits

Reviewing files that changed from the base of the PR and between d33d33e and 822dc6f.

📒 Files selected for processing (4)
  • backend/auth.py
  • backend/main.py
  • src/lib/api.ts
  • src/pages/AuthPage.tsx

Comment thread backend/auth.py
Comment on lines +46 to +48
except Exception as e:
# Any other error means the refresh failed
raise HTTPException(status_code=401, detail=f"Token refresh failed: {str(e)}")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Avoid exposing internal error details in the response.

Including str(e) in the HTTP response could leak sensitive information about the backend (e.g., database connection strings, internal paths, or Supabase error internals). Return a generic message instead.

🛡️ Proposed fix
     except Exception as e:
         # Any other error means the refresh failed
-        raise HTTPException(status_code=401, detail=f"Token refresh failed: {str(e)}")
+        print(f"Token refresh failed: {e}")  # Log internally for debugging
+        raise HTTPException(status_code=401, detail="Token refresh failed")
🤖 Prompt for 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.

In `@backend/auth.py` around lines 46 - 48, The except block that currently does
"except Exception as e: raise HTTPException(status_code=401, detail=f'Token
refresh failed: {str(e)}')" should be changed to avoid leaking internal error
text: replace the HTTPException detail with a generic message like "Token
refresh failed" or "Unauthorized", and log the original exception internally
using an application logger (e.g., logger.exception or logger.error with the
exception) before raising HTTPException; keep the status_code=401 and continue
to raise HTTPException but do not include str(e) in the response.

Comment thread src/lib/api.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/lib/api.ts (1)

69-97: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Critical: Duplicate function definitions break the file.

The code contains two handleResponse function declarations where the first one (line 69) is never closed before the second one (line 85) begins. This is a parse error that will prevent the module from loading.

Additionally:

  • Line 122 calls handleResponse(retryRes, true) with two arguments, but the second definition (line 85) only accepts one parameter
  • The static analysis errors ("Illegal use of an export declaration not at the top level" at lines 149+) confirm that subsequent code is nested inside the unclosed first function

This appears to be an incomplete merge or edit where the old handleResponse (lines 69-82) should have been removed. The past review flagged the 401 logic in handleResponse as unreachable since safeFetch already handles it.

🐛 Proposed fix — remove the stale function fragment
 // ── Shared Error Handling Logic ──────────────────────────────────────────────
 
-// ── Shared Error Handling Logic ──────────────────────────────────────────────
-
-async function handleResponse(res: Response, retried = false): Promise<Response> {
-  if (res.ok) return res;
-
-  // Handle 401 — try to refresh token and retry once
-  if (res.status === 401 && !retried) {
-    const refreshed = await tryRefreshToken();
-    if (!refreshed) {
-      clearToken();
-      window.location.href = '/auth';
-      throw new Error('Session expired. Redirecting to login.');
-    }
-    // Return a special signal to retry
-    return res;
-  }
-
-  // Handle 5xx errors (Server Side)
 async function handleResponse(res: Response): Promise<Response> {
   if (res.ok) return res;
 
+  // 401 is handled by safeFetch before reaching here; this is a fallback
+  if (res.status === 401) {
+    clearToken();
+    window.location.href = '/auth';
+    throw new Error('Session expired. Redirecting to login.');
+  }
+
   if (res.status >= 500) {
     const msg = "Server error. Please try again later.";
     toast.error(msg);

Also update the call site in safeFetch:

-      return await handleResponse(retryRes, true);
+      return await handleResponse(retryRes);
🤖 Prompt for 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.

In `@src/lib/api.ts` around lines 69 - 97, Remove the stale/duplicate
handleResponse fragment that begins with "async function handleResponse(res:
Response, retried = false)" so there is only one handleResponse implementation;
ensure the remaining handleResponse signature matches its call sites (safeFetch
should call handleResponse(retryRes) or update calls to pass the retried flag
consistently), and remove any nested code that resulted from the unclosed
function (verify tryRefreshToken, clearToken logic is handled in safeFetch and
not duplicated here). Locate occurrences of handleResponse, safeFetch,
tryRefreshToken, and clearToken in the file to remove the old fragment and
reconcile the single canonical handleResponse implementation with existing
callers.
🧹 Nitpick comments (1)
src/lib/api.ts (1)

196-217: 💤 Low value

scanOnline bypasses 401 refresh logic.

This function uses raw fetch() instead of safeFetch(), so if the access token expires mid-session, users will receive an error instead of automatic token refresh and retry.

If the intent is to keep this lightweight for the fallback path, consider at least handling 401 explicitly:

       if (!res.ok) {
+        // Let caller handle auth expiry like other endpoints
+        if (res.status === 401) {
+          throw new Error('Unauthorized');
+        }
         const err = await res.json().catch(() => ({ detail: res.statusText }));
         throw new Error((err as { detail?: string }).detail || `HTTP ${res.status}`);
       }

Alternatively, if silent refresh is desired here too, switch to safeFetch() and catch the redirect behavior.

🤖 Prompt for 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.

In `@src/lib/api.ts` around lines 196 - 217, The scanOnline function currently
calls fetch directly which bypasses the app's token-refresh logic; update
scanOnline to use the existing safeFetch wrapper (or implement explicit 401
handling) so expired access tokens are refreshed and the request is retried:
specifically, replace the direct fetch call inside scanOnline with a call to
safeFetch(`${API_BASE}/api/v1/scan-auto`, { method: 'POST', headers:
authHeaders(), body: form }) or, if you prefer minimal change, catch a 401
response from the fetch, invoke the token refresh routine and retry the same
request once; ensure the error handling behavior (returning null on network
TypeError and propagating non-401 server errors like NOT_A_FISH) is preserved.
🤖 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.

Outside diff comments:
In `@src/lib/api.ts`:
- Around line 69-97: Remove the stale/duplicate handleResponse fragment that
begins with "async function handleResponse(res: Response, retried = false)" so
there is only one handleResponse implementation; ensure the remaining
handleResponse signature matches its call sites (safeFetch should call
handleResponse(retryRes) or update calls to pass the retried flag consistently),
and remove any nested code that resulted from the unclosed function (verify
tryRefreshToken, clearToken logic is handled in safeFetch and not duplicated
here). Locate occurrences of handleResponse, safeFetch, tryRefreshToken, and
clearToken in the file to remove the old fragment and reconcile the single
canonical handleResponse implementation with existing callers.

---

Nitpick comments:
In `@src/lib/api.ts`:
- Around line 196-217: The scanOnline function currently calls fetch directly
which bypasses the app's token-refresh logic; update scanOnline to use the
existing safeFetch wrapper (or implement explicit 401 handling) so expired
access tokens are refreshed and the request is retried: specifically, replace
the direct fetch call inside scanOnline with a call to
safeFetch(`${API_BASE}/api/v1/scan-auto`, { method: 'POST', headers:
authHeaders(), body: form }) or, if you prefer minimal change, catch a 401
response from the fetch, invoke the token refresh routine and retry the same
request once; ensure the error handling behavior (returning null on network
TypeError and propagating non-401 server errors like NOT_A_FISH) is preserved.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2e46c221-4c17-4eda-b8ca-6c8f274849a7

📥 Commits

Reviewing files that changed from the base of the PR and between 822dc6f and e57e36a.

📒 Files selected for processing (3)
  • backend/auth.py
  • backend/main.py
  • src/lib/api.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • backend/main.py
  • backend/auth.py

@jpdevhub jpdevhub left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Their are some major issues in the code fix this with some suggestions.

  1. src/lib/api.ts - Duplicate Function Declaration (Syntax Error)
  2. backend/auth.py - Unsafe Environment Variable Access
    Fix: They should change os.environ["SUPABASE_URL"] to os.environ.get("SUPABASE_URL") (and the same for the key), and explicitly raise an HTTPException(status_code=500) if they are None.
  3. src/lib/api.ts - Conflicting Retry Logic
    Fix: They should centralize the token refresh logic. Either safeFetch catches the 401 and retries the request, OR handleResponse throws a specific error that safeFetch catches to trigger the retry. Having it in both places will cause race conditions and duplicate API calls.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BUG-01: Session terminates after 1 hour (Implement Refresh Token Flow)

2 participants