Skip to content

feat: configurable session initialization to prevent race condition#131

Open
mandarini wants to merge 2 commits intomainfrom
fix/async-token-refresh-race-condition
Open

feat: configurable session initialization to prevent race condition#131
mandarini wants to merge 2 commits intomainfrom
fix/async-token-refresh-race-condition

Conversation

@mandarini
Copy link
Contributor

@mandarini mandarini commented Oct 14, 2025

Summary

Adds a sessionInitialization configuration option to createServerClient that gives users control over automatic session initialization while maintaining backward compatibility.

Changes

New sessionInitialization Option

Three modes available:

  • 'auto' (default) - Automatic initialization on client creation (current behavior)
  • 'manual' - Requires explicit await supabase.auth.initialize() call
  • false - No automatic initialization (for read-only contexts)

New Methods

  • supabase.auth.initialize() - Manually trigger initialization (idempotent)
  • supabase.auth.isInitialized() - Check if initialization has completed

Usage Examples

Auto mode (default, backward compatible):

const supabase = createServerClient(url, key, {
  cookies: { getAll, setAll },
});
// Ready to use - initialization happens automatically

Manual mode (explicit control):

const supabase = createServerClient(url, key, {
  cookies: { getAll, setAll },
  sessionInitialization: "manual",
});

if (requiresAuth) {
  await supabase.auth.initialize();
}

Disabled mode (read-only):

const supabase = createServerClient(url, key, {
  cookies: { getAll }, // no setAll
  sessionInitialization: false,
});

Breaking Changes

None - fully backward compatible.

Summary by CodeRabbit

  • New Features

    • Session initialization now configurable with three modes: Auto (default), Manual, and Disabled
    • Added isInitialized() method to check session initialization status
    • Added initialize() method for manual session initialization
  • Documentation

    • New Session Initialization section in README with examples for each mode
  • Bug Fixes

    • Improved error logging for cookie-setting failures after response is sent
  • Tests

    • Added tests for session initialization modes and error handling scenarios

@mandarini mandarini force-pushed the fix/async-token-refresh-race-condition branch from dc11250 to 9c556eb Compare October 14, 2025 10:48
@mandarini mandarini requested a review from hf October 14, 2025 14:22
@mandarini mandarini self-assigned this Oct 14, 2025
@ixxie
Copy link

ixxie commented Dec 14, 2025

@mandarini thanks for opening this, I'm running into this issue since I had to upgrade from Vite 5 => 7 in a SvelteKit project.

Important

Svelte and SvelteKit are moving really fast right now, since they have experimental support for async Svelte and SvelteKit remote functions. So SvelteKit users that want to keep dependencies up to day have to bump Vite to 7.

Is there a workaround I could do to the recommended approach from the docs to sidestep the issue? The best I could come up with (with Claude's help) is:

export const handle: Handle = async ({ event, resolve }) => {
  // Creates a Supabase client specific to this server request.
  // The Supabase client gets the Auth token from the request cookies.
  event.locals.supabase = createServerClient(
    PUBLIC_SUPABASE_URL,
    PUBLIC_SUPABASE_ANON_KEY,
    {
      cookies: {
        getAll: () => event.cookies.getAll(),
        // SvelteKit requires cookie `path` to be explicity set
        setAll: (cookiesToSet) => {
          cookiesToSet.forEach(({ name, value, options }) => {
            event.cookies.set(name, value, { ...options, path: '/' });
          });
        },
      },
    },
  );

  // Workaround for supabase/ssr#131: trigger token refresh early
  // before resolve() to prevent race condition with Vite 7
  await event.locals.supabase.auth.getSession();

  // Unlike `supabase.auth.getSession()`, which returns the session _without_
  // validating the JWT, this function also calls `getUser()` to validate the
  // JWT before returning the session.
  event.locals.safeGetSession = async () => {
    const {
      data: { session },
    } = await event.locals.supabase.auth.getSession();
    // no session
    if (!session) {
      return { session: null, user: null };
    }

    const {
      data: { user },
      error,
    } = await event.locals.supabase.auth.getUser();
    // no jwt token
    if (error) {
      return { session: null, user: null };
    }

    return { session, user };
  };

  return resolve(event, {
    filterSerializedResponseHeaders(name) {
      // forward supabase headers
      return name === 'content-range' || name === 'x-supabase-api-version';
    },
  });
};

@mandarini
Copy link
Contributor Author

Summoning @hf, maybe some better solution for @ixxie ?

@jackien1
Copy link

pls help

@j4w8n
Copy link
Contributor

j4w8n commented Jan 21, 2026

Can someone add more context for this issue? I've got a branch running vite 7 and async svelte, but I've not run across this issue.

@jackien1
Copy link

Can someone add more context for this issue? I've got a branch running vite 7 and async svelte, but I've not run across this issue.

Having this issue with cookies when using Social Login with callback endpoint like /auth/callback. Working if you're not using any callback endpoint and redirecting to /.

@jackien1
Copy link

Can someone add more context for this issue? I've got a branch running vite 7 and async svelte, but I've not run across this issue.

are you using callback endpoint or root redirect?

@j4w8n
Copy link
Contributor

j4w8n commented Jan 29, 2026

Can someone add more context for this issue? I've got a branch running vite 7 and async svelte, but I've not run across this issue.

are you using callback endpoint or root redirect?

Using a callback endpoint. All auth things are server side

@mandarini mandarini requested a review from hf January 29, 2026 15:15
@mandarini mandarini force-pushed the fix/async-token-refresh-race-condition branch from 9c556eb to 120b48c Compare February 16, 2026 15:45
@coderabbitai
Copy link

coderabbitai bot commented Feb 16, 2026

No actionable comments were generated in the recent review. 🎉


📝 Walkthrough

Walkthrough

This PR adds configurable session initialization to the server client via a new sessionInitialization option (values: 'auto' (default), 'manual', or false), exposes auth.initialize() and auth.isInitialized(), and includes related tests, docs, and improved cookie error handling.

Changes

Cohort / File(s) Summary
Documentation
README.md
Added "Session Initialization" section with sessionInitialization option details, examples for auto/manual/false, and isInitialized() usage.
Type Definition
src/types.ts
Added exported SessionInitializationMode type (`'auto'
Core Feature Implementation
src/createServerClient.ts
Added sessionInitialization?: SessionInitializationMode to overloads/options; attached auth.initialize() and auth.isInitialized() with internal _initialized and _initPromise; implemented non-blocking automatic initialization when mode is 'auto'.
Error Handling
src/cookies.ts
Wrapped setAll in try/catch; detect and log helpful errors when cookies are set after response is sent; rethrow non-response-related errors unchanged.
Tests
src/createServerClient.spec.ts, src/cookies.spec.ts
Added tests for proactive session initialization (auto/manual/disabled, concurrency, isInitialized) and for cookie-set error scenarios (post-response logging vs other errors).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% 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 accurately and specifically describes the main change: introducing configurable session initialization modes to address a race condition in async token refresh.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/async-token-refresh-race-condition

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

@mandarini mandarini force-pushed the fix/async-token-refresh-race-condition branch from 120b48c to 182343e Compare February 16, 2026 15:48
@mandarini mandarini changed the title fix: prevent race condition in async token refresh feat: prevent race condition in async token refresh Feb 16, 2026
@mandarini mandarini changed the title feat: prevent race condition in async token refresh feat: configurable session initialization to prevent race condition Feb 16, 2026
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@README.md`:
- Around line 26-86: The TS example in the README is missing a comma after the
cookies object in the createServerClient call and the fenced error block lacks a
language tag; update the snippet near createServerClient so the options object
reads `cookies: { getAll, setAll },` and add a language identifier (e.g.,
```text) to the error fence `Error: Cannot use \`cookies.set(...)\` after the
response has been generated`; after making these edits run Prettier/formatting
on README.md to clear the CI warning.

In `@src/types.ts`:
- Around line 69-76: Prettier is complaining about quote style for the
SessionInitializationMode type; update the string literals in the exported type
SessionInitializationMode in src/types.ts to use the project's preferred quotes
(e.g. change 'auto' | 'manual' to "auto" | "manual") or simply run the project's
Prettier formatter to reformat the file so the union uses the consistent quote
style while keeping the boolean false unchanged.

@mandarini mandarini requested a review from fadymak February 16, 2026 16:31
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.

5 participants