supabase icon indicating copy to clipboard operation
supabase copied to clipboard

Auth session is not persistent

Open bart opened this issue 2 years ago • 45 comments

Version

@nuxtjs/supabase: ^1.0.2 nuxt: ^3.7.1

Reproduction Link

https://github.com/bart/test-supabase

Steps to reproduce

  1. Add SUPABASE_URL and SUPABASE_KEY to your .env file
  2. npm run dev
  3. localhost:3000 should redirect you to the login page. Enter username and password and click Login button
  4. You should be redirected to /
  5. Give it a refresh and you are logged out (redirected back to /login) again

What is Expected?

Auth session should be persistent. Auth guard should not redirect to login page as long session isn't expired or user does not sign out manually.

What is actually happening?

Auth guard redirects back to login page assuming user isn't logged in.

bart avatar Sep 06 '23 11:09 bart

I can confirm that this happens since v1.0.0

phillipmohr avatar Sep 09 '23 09:09 phillipmohr

Same here, it is a problem

Dmytro-Tihunov avatar Sep 12 '23 13:09 Dmytro-Tihunov

Same here. Version 1.1.0 and 1.0.0. But sometimes i get slightly different behavior. On first login it redirects me to '/' and oAuth token is present in applications.

But after logging out, then i try to login again, i got redirected back to login page. And no token is present in application. The only thing i can see is auth-token-code-verifier.

ivanushkaPr avatar Sep 12 '23 20:09 ivanushkaPr

Same issue here.

tomasmorello avatar Sep 13 '23 04:09 tomasmorello

I think the reason for that redirect is an empty user object (null) loaded in the middleware on server-side page loads (for example on a hard refresh). You can verify it by using a custom middleware and logging useSupabaseUser() On client side it's value is set but on server-side it is null.

A work around would be a custom middleware that only gets executed on client side but in my opinion that's not a rock solid solution:

// /middleware/auth.js
export default defineNuxtRouteMiddleware((to, _from) => {
    const user = useSupabaseUser()
    if (!user.value && process.client) {
        return navigateTo('/login')
    }
})

If I also add a custom server-side auth middleware (in /server/middleware folder) that tries to load await serverSupabaseUser(event) I'm getting the same error as described in https://github.com/nuxt-modules/supabase/issues/238

I think we need a solution for this issue as soon as possible. Otherwise this Nuxt module doesn't make any sense - at least for authentication.

bart avatar Sep 13 '23 09:09 bart

Same issue here and I'm using the implicit flow.

I've attached some screenshots of the Analytics being reported locally.

When a page is refreshed, we get redirected to /login. After a second my watch function kicks in and then redirects me to /

  watch(
    user,
    () => {
      if (user.value) {
        return navigateTo('/')
      }
    },
    { immediate: true }
  )

It makes local development a massive pain.

The number of strange behaviours has been multiplying the past few weeks, but I presume it's because of recent updates at supabases end too.

CleanShot 2023-09-14 at 16 01 19@2x CleanShot 2023-09-14 at 16 01 11@2x

philliphartin avatar Sep 14 '23 05:09 philliphartin

Posting here as well since it seems related, I just put up PR https://github.com/nuxt-modules/supabase/pull/272 to address the invalid claim: missing sub claim I was getting that seemed to be caused by a race condition in the useSupabaseUser composable when making an API call right after login that tried to read the user cookie to get some user data on an internal API route.

By changing the useSupabaseUser composable to an async function and properly handling the promise called within it, I eliminated the errors I was seeing on my end.

I updated my example "confirmation" code from the example to this:

const user = await useSupabaseUser();
watch(
  user,
  async () => {
    if (user.value) {
      const { data } = await useFetch("/api/user", useCookieHeader());
      console.log(data);
    }
  },
  { immediate: true }
);

I properly received the data from the endpoint I no longer had these issues


Hopefully this helps anyone else currently stuck on this

ammuench avatar Sep 22 '23 04:09 ammuench

@ammuench's async useSupabaseUser solved MFA headaches for me in SSR.

jawngee avatar Oct 01 '23 07:10 jawngee

I lifted his pull request into a composable that I use now instead of useSupabaseUser:

import type { User } from "@supabase/supabase-js";
import { useState } from "#imports";

export default async function useAsyncSupabaseUser() {
  const supabase = useSupabaseClient();

  const user = useState<User | null>("supabase_user", () => null);
  const sessionData = await supabase?.auth.getSession();
  const session = sessionData.data.session;

  if (session) {
    if (JSON.stringify(user.value) !== JSON.stringify(session.user)) {
      user.value = session.user;
    } else {
      user.value = null;
    }
  }

  return user as Ref<User | null>;
}

Thanks @ammuench!

jawngee avatar Oct 01 '23 07:10 jawngee

But it still means we have to use a custom guard, right?

bart avatar Oct 04 '23 23:10 bart

Same issue here I think. Anything new?

Issues:

  1. Getting redirected to '/login' after login
  2. When I try await serverSupabaseUser(event) I get invalid claim: missing sub claim - see #238

I tried a bunch of stuff and and also looked back into an older project where it is working. I think the issue here is that the sb-access-token and sb-refresh-token cookie do not get set. Can anyone with more experience can confirm this?

Without this working this Nuxt module doesn't make any sense for authentication, like @bart mentioned earlier.

Edit: I added this code to my new and my older project as app.vue. In my Older Project cookies get set, in my newer project not.

<template>
  {{ data }}
</template>

<script setup lang="ts">
const supabase = useSupabaseClient()

const { data, error } = await supabase.auth.signInWithPassword({
  email: "[email protected]",
  password: "password"
})
</script>

Version older Project: 0.3.8 Version newer Project: 1.1.3

guarenty avatar Oct 30 '23 07:10 guarenty

I have this issue as well, any fixes for this?

fdarkaou avatar Nov 06 '23 13:11 fdarkaou

@fdarkaou I don't know if it is a proper fix, but it is working for me. See #300

guarenty avatar Nov 06 '23 13:11 guarenty

Hello everyone, module maintainer here. Apologies for the delay; I've finally found the time to address this issue. I just need to confirm something regarding this bug: I'm currently only able to reproduce it on Safari and in dev environments. If there are additional scenarios where this bug occurs, please let me know.

Additionally, if you believe you have a solution for the issue, feel free to create a pull request. I'll be able to expedite the process of merging and releasing the fix if I don't have to debug it myself and can simply review the solution. Your contributions are highly appreciated 😁

larbish avatar Nov 17 '23 10:11 larbish

Seems like Safari is not accepting secure cookies on localhost. So for users that want to use Safari for local development, you can fix this bug by setting cookieOptions.secure to false in your nuxt.config.ts file:

export default defineNuxtConfig({
  supabase: {
    cookieOptions: {
      secure: process.env.NODE_ENV === 'production',
    }
})

larbish avatar Nov 17 '23 12:11 larbish

I'm currently only able to reproduce it on Safari and in dev environments. If there are additional scenarios where this bug occurs, please let me know.

For me, this bug also happens in Google Chrome

phillipmohr avatar Nov 18 '23 06:11 phillipmohr

Can you provide a reproduction please? Is it happening only in dev mode? Is my previous message fixing it? I'm only able to reproduce on dev and safari with @bart reproduction and it's fixed by disabling secure cookies on localhost.

larbish avatar Nov 20 '23 08:11 larbish

I have the same issue in Brave browser, I tried in chromium and it worked, I think it has to do we useCookie not working for some reason when third party cookies are disabled in the browser settings

fanckush avatar Nov 22 '23 11:11 fanckush

I have the same issues, tested in Firefox and Chrome. Cookies aren't set and I get status 500 invalid claim: missing sub claim

I'm new to Nuxt and programming (<2years) and I don't know what to do.

tried to set cookieOptions.secure to false

supabase: {
    redirect: false,
    cookieOptions: {
      secure: false,
    },

versions: (downgraded to 1.1.2 but didn't work)

"@nuxtjs/supabase": "^1.1.2",
    "nuxt": "^3.8.0",

creating a User and login in does work (I got the session data and user data back).

nonInfelix avatar Nov 23 '23 21:11 nonInfelix

I can reproduce this on Mobile (chrome, android) when running nuxt with --host Browser indicates that the connection is not secure, and cookies seem to be ignored if I don't set them as secure: false

noook avatar Nov 24 '23 10:11 noook

Seems like Safari is not accepting secure cookies on localhost. So for users that want to use Safari for local development, you can fix this bug by setting cookieOptions.secure to false in your nuxt.config.ts file:

export default defineNuxtConfig({
  supabase: {
    cookieOptions: {
      secure: process.env.NODE_ENV === 'production',
    }
})

For me this worked! I was racking my brains because in Microsoft Edge it didn't present a problem and in Chrome it did ( 119.0.6045.160 / windows 11).

I'm using "@nuxtjs/supabase": "^1.1.4", dev enviroment

For anyone making this configuration change, remember to clear your cookies and local storage before testing again.

luizzappa avatar Nov 25 '23 15:11 luizzappa

Seems like Safari is not accepting secure cookies on localhost. So for users that want to use Safari for local development, you can fix this bug by setting cookieOptions.secure to false in your nuxt.config.ts file:

export default defineNuxtConfig({
  supabase: {
    cookieOptions: {
      secure: process.env.NODE_ENV === 'production',
    }
})

For me this worked! I was racking my brains because in Microsoft Edge it didn't present a problem and in Chrome it did ( 119.0.6045.160 / windows 11).

I'm using "@nuxtjs/supabase": "^1.1.4", dev enviroment

For anyone making this configuration change, remember to clear your cookies and local storage before testing again.

Whats the difference between this and setting secure to false ?

nonInfelix avatar Nov 27 '23 17:11 nonInfelix

@nonInfelix , it's just good practice. Working with environment variables (and .env files) allows you to change settings between environments (production, development, ...) without having to change several parts of your code

luizzappa avatar Nov 27 '23 20:11 luizzappa

Any updates if there will be a fix for this forthcoming?

AdamBD avatar Jan 17 '24 17:01 AdamBD

For what it's worth, this and other auth issues forced me down a path of just using the standard js client library. It's very simple to integrate into a composable and I've had no issues.

On Wed, Jan 17, 2024 at 12:43 PM Adam Ben-David @.***> wrote:

Any updates if there will be a fix for this forthcoming?

— Reply to this email directly, view it on GitHub https://github.com/nuxt-modules/supabase/issues/264#issuecomment-1896294511, or unsubscribe https://github.com/notifications/unsubscribe-auth/AATJJCG6L2RMTKUQ2DXQQM3YPAEU5AVCNFSM6AAAAAA4NGGYBWVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTQOJWGI4TINJRGE . You are receiving this because you are subscribed to this thread.Message ID: @.***>

MikeBman avatar Jan 17 '24 18:01 MikeBman

@MikeBman would you care to share your composable?

AdamBD avatar Jan 17 '24 20:01 AdamBD

My composable was very specific to my needs. Here is a stripped down version...honestly not sure if this follows best practice or not, but it's working well in production:

import { createClient } from '@supabase/supabase-js'

const supabase = createClient("url", "key");

const logged_in = ref(false);
const user_id = ref();
const user_meta = reactive({
    first_name: '',
    last_name: '',
    state: ''
});

export const useUserMeta = () => {

    const getUserMeta = async () => {
        if (logged_in.value == true) {
            return;
        }
        try {
            const { data, error } = await supabase.auth.getSession();
            Object.assign(user_meta, data.session.user.user_metadata);
            logged_in.value = true;
            user_id.value = data.session.user.id;
        } catch (error) {
            console.log("ERROR fetching user:", error);
        }
    }

    const saveUserMeta = async () => {
        try {
            const { data, error } = await supabase.auth.updateUser({
                data: user_meta
            });

            if (error) throw error;

        } catch (error) {
            console.log("ERROR saving user:", error);
        } finally {
            console.log('USER UPDATED');
        }
    }

    const handleLogin = async (email) => {
        try {
            const { error } = await supabase.auth.signInWithOtp({
                email: email,
                options: {
                    emailRedirectTo: 'http://example.com/',
                }
            })
            if (error) throw error
            else return true
        } catch (error) {
            console.log(error.error_description || error.message);
        }
    }
  
    return {
        user_id,
        user_meta,
        getUserMeta,
        saveUserMeta,
        handleLogin
    };
}```

MikeBman avatar Jan 18 '24 03:01 MikeBman

@MikeBman when trying this pattern I am getting the following warning from supabase in the client

image

Do you have this as well?

AdamBD avatar Jan 19 '24 17:01 AdamBD

@AdamBD No. Perhaps it's remnant of using the old module? Maybe try clearing your local storage and cookies?

MikeBman avatar Jan 19 '24 18:01 MikeBman

@MikeBman was cause I still had the old nuxt/supabase module loading.

Question - Are you doing middleware redirects based on the users state? I am having issues getting that to work now as the user state is not available on SSR and its causing hydration missmatches on the client when doing client-side redirecting after checking the local storage

AdamBD avatar Jan 20 '24 17:01 AdamBD