supabase / auth-helpers

A collection of framework specific Auth utilities for working with Supabase.
https://supabase.github.io/auth-helpers/
MIT License
902 stars 237 forks source link

[Sveltekit] Setup according to docs causes infinite loop with invalidation in onAuthStateChange #477

Closed bhark closed 1 year ago

bhark commented 1 year ago

StackBlitz

I've set up a Stackblitz with the same issue here. Create a .env and link it with a stock Supabase project of yours.

Describe the bug

After signing in using valid credentials, the invalidate('supabase:auth') in +layout.svelte is called rapidly, many times a second, until the API locks you out due to the rate limit being exceeded and the session is destroyed. If invalidate('supabase:auth') is commented out and replaced with a console.log, it is fired about every other second and the session is maintained without issues.

Network log shows a flood of (successful) requests to /auth/v1/token?grant_type=refresh_token, until the rate limiter steps in.

Steps To Reproduce

Steps to reproduce the behavior, please provide code snippets or a repository:

  1. Set up a new Sveltekit project (npm create svelte@latest new-project)
  2. Follow the Sveltekit auth helper docs here or here precisely
  3. Set up a form action to sign in, also as described in the docs
  4. Create a +page.svelte with a form element and submit the sign in form using valid credentials

Expected behavior

After setting up as according to the documentation here or here, I should be able to sign in and have a healthy session, which should be kept in sync and be available both server-side and client-side.

System information

Additional context

No typescript, no extra packages.

silentworks commented 1 year ago

I can't replicate this in any of the example repos I have. Also the stackblitz example doesn't work because there are CORS issues with stackblitz. You can view one of the example repo here https://github.com/supabase-community/supabase-by-example/tree/main/reset-flow/sveltekit

bhark commented 1 year ago

@silentworks Thanks for looking into this. I tried cloning the repo you linked, and setting up a new, blank supabase project, and am getting a different issue (which seems kind of related):

Signing up and clicking the magic link results in /logging-in?redirect=/# hanging, and this error:

[1] AuthRetryableFetchError: fetch failed
[1]     at /home/akhefale/tetrabit/sb-test/node_modules/@supabase/gotrue-js/dist/main/lib/fetch.js:30:16
[1]     at Generator.next (<anonymous>)
[1]     at /home/akhefale/tetrabit/sb-test/node_modules/@supabase/gotrue-js/dist/main/lib/fetch.js:8:71
[1]     at new Promise (<anonymous>)
[1]     at __awaiter (/home/akhefale/tetrabit/sb-test/node_modules/@supabase/gotrue-js/dist/main/lib/fetch.js:4:12)
[1]     at handleError (/home/akhefale/tetrabit/sb-test/node_modules/@supabase/gotrue-js/dist/main/lib/fetch.js:27:40)
[1]     at /home/akhefale/tetrabit/sb-test/node_modules/@supabase/gotrue-js/dist/main/lib/fetch.js:87:35
[1]     at processTicksAndRejections (node:internal/process/task_queues:96:5) {
[1]   __isAuthError: true,
[1]   status: 0
[1] }

Manually navigating to /auth/signin afterwards results in this error:

[1] AuthApiError: Invalid Refresh Token
[1]     at /home/akhefale/tetrabit/sb-test/node_modules/@supabase/gotrue-js/dist/main/lib/fetch.js:41:20
[1]     at processTicksAndRejections (node:internal/process/task_queues:96:5) {
[1]   __isAuthError: true,
[1]   status: 400
[1] }

Trying to log in, then, results in the original error:

[1] AuthApiError: Rate limit exceeded
[1]     at /home/akhefale/tetrabit/supabase-by-example/reset-flow/sveltekit/node_modules/@supabase/gotrue-js/dist/main/lib/fetch.js:41:20
[1]     at processTicksAndRejections (node:internal/process/task_queues:96:5) {
[1]   __isAuthError: true,
[1]   status: 429
[1] }

The refresh token being sent seems valid to me.

huntabyte commented 1 year ago

I'm also experiencing an infinite invalidation loop in onAuthStateChange except it occurs when an error boundary is rendered @silentworks. I discovered it by clicking a link to a page in my app that doesn't exist. Currently trying to determine the root cause of this and will report back if I'm able to identify it this evening.

Reproduction Repo: StackBlitz - Just add a .env with PUBLIC_SUPABASE_ANON_KEY & PUBLIC_SUPABASE_URL

Manual Repro Steps:

  1. Setup everything exactly as stated in the documentation
  2. In the root +layout.svelte, add a link to a page that doesn't exist (/whatever).
  3. Click the '/whatever' <a> element
  4. Experience infinite loop of invalidate('supabase:auth')
ralphwest1 commented 1 year ago

I'm running into the same issue described above.

Whenever hitting a 404 page. it starts an infinite loop of invalidating invalidate('supabase:auth') and then reloading over and over.

If I comment out invalidate('supabase:auth') in ./src/routes/+layout.svelte and restart the project the infinite ceases.

ralphwest1 commented 1 year ago

@silentworks I was able to recreate the issue in the example repo you reference: https://github.com/supabase-community/supabase-by-example/tree/main/reset-flow/sveltekit by upgrading @supabase/supabase-js from v2.10.0 to v2.12.1 with pnpm i @supabase/supabase-js@latest

Issue seems to be directly related to underlying @supabase/gotrue-js going from v2.15.0 to v2.16.0

https://github.com/supabase/gotrue-js/releases/tag/v2.16.0

To recreate

  1. clone repo: https://github.com/supabase-community/supabase-by-example/tree/main/reset-flow/sveltekit
  2. pnpm i @supabase/supabase-js@latest
  3. pnpm dev
  4. navigate to any route that will produce a 404 error such as: http://localhost:5174/not-there

Temp fix until a patch

@huntabyte, @bhark

For other running into this issue you can resolve by overriding the dependant @supabase/gotrue-js version in your package json.

pnpm: https://pnpm.io/package_json#pnpmoverrides npm: https://docs.npmjs.com/cli/v9/configuring-npm/package-json#overrides

The settings for overriding a nested depend package varies based on:

For example if you are using pnpm with a monorepo and have v2.12.1 of @supabase/supabase-js. You would add the below to your root package.json:

{
...
"pnpm": {
    "overrides": {
      "@supabase/supabase-js@2.12.1>@supabase/gotrue-js": "2.15.0"
    }
  }
}
adonis2611 commented 1 year ago

@ralphwest1 tried this and still got the same issue

ralphwest1 commented 1 year ago

@adonis2611

The settings for overriding a nested depend package varies based on:

pnpm: https://pnpm.io/package_json#pnpmoverrides npm: https://docs.npmjs.com/cli/v9/configuring-npm/package-json#overrides

bhark commented 1 year ago

I think we're talking about two different issues here, although they both seem to stem from gotrue.

It turns out that the main issue i posted can't be reliably reproduced. A colleague of mine with the exact same version of node and npm was able to run my codebase as is without issue, and after reinstalling Arch I can too.

The 404 loop that @ralphwest1 and @huntabyte is talking about can apparently be reliably reproduced. My colleague had this issue too, and I have it on my new system.

silentworks commented 1 year ago

I'm also experiencing an infinite invalidation loop in onAuthStateChange except it occurs when an error boundary is rendered @silentworks. I discovered it by clicking a link to a page in my app that doesn't exist. Currently trying to determine the root cause of this and will report back if I'm able to identify it this evening.

Reproduction Repo: StackBlitz - Just add a .env with PUBLIC_SUPABASE_ANON_KEY & PUBLIC_SUPABASE_URL

Manual Repro Steps:

1. Setup everything exactly as stated in the documentation

2. In the root `+layout.svelte`, add a link to a page that doesn't exist (/whatever).

3. Click the '/whatever' `<a>` element

4. Experience infinite loop of `invalidate('supabase:auth')`

@ralphwest1 and @huntabyte yeah I can replicate this. You can fix it by updating the script section to be this instead, we will update the guides soon.

        import { invalidate } from '$app/navigation';
    import { onMount } from 'svelte';
    import type { LayoutData } from './$types';

    export let data: LayoutData;

    $: ({ supabase, session } = data);

    onMount(() => {
        const {
            data: { subscription }
        } = supabase.auth.onAuthStateChange((event, _session) => {
            if (_session?.expires_at !== session?.expires_at) {
                invalidate('supabase:auth');
            }
        });

        return () => {
            subscription.unsubscribe();
        };
    });
huntabyte commented 1 year ago

Awesome, thanks for the swift response & solution!

kvetoslavnovak commented 1 year ago

Having the same problem here. Going to non existing route causes the infinite loop. The problem seems to be caused by invalidate('supabase:auth') in +layout.svelte file. If I comment invalidate('supabase:auth') out the loop stops to occur.

EDIT: There is already a solution hereabove which I have overlooked. THANK YOU VERY MUCH.