nuxt-modules / supabase

Supabase module for Nuxt.
https://supabase.nuxtjs.org
MIT License
705 stars 129 forks source link

Auth session is not persistent #264

Open bart opened 1 year ago

bart commented 1 year ago

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.

phillipmohr commented 1 year ago

I can confirm that this happens since v1.0.0

Dmytro-Tihunov commented 1 year ago

Same here, it is a problem

ivanushkaPr commented 1 year ago

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.

tomasmorello commented 1 year ago

Same issue here.

bart commented 1 year ago

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.

philliphartin commented 1 year ago

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
ammuench commented 1 year ago

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

jawngee commented 1 year ago

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

jawngee commented 1 year ago

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!

bart commented 1 year ago

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

guarenty commented 1 year ago

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: "tester@test.de",
  password: "password"
})
</script>

Version older Project: 0.3.8 Version newer Project: 1.1.3

fdarkaou commented 1 year ago

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

guarenty commented 1 year ago

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

larbish commented 12 months ago

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 commented 12 months ago

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',
    }
})
phillipmohr commented 12 months ago

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

larbish commented 11 months ago

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.

fanckush commented 11 months ago

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

nonInfelix commented 11 months ago

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).

noook commented 11 months ago

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

luizzappa commented 11 months ago

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.

nonInfelix commented 11 months ago

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 ?

luizzappa commented 11 months ago

@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

AdamBD commented 10 months ago

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

MikeBman commented 10 months ago

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: @.***>

AdamBD commented 10 months ago

@MikeBman would you care to share your composable?

MikeBman commented 10 months ago

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
    };
}```
AdamBD commented 9 months ago

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

image

Do you have this as well?

MikeBman commented 9 months ago

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

AdamBD commented 9 months ago

@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

MikeBman commented 9 months ago

@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

I am using middleware redirects but it's client side only.

AdamBD commented 9 months ago

@MikeBman thanks, do you mind sharing a code snippet of a middleware redirect?

MikeBman commented 9 months ago

@AdamBD I'm happy to help, but I don't want to turn this thread into an off-topic support forum.

The example middleware in the docs is a great reference:

export default defineNuxtRouteMiddleware(to => {
  // skip middleware on server
  if (process.server) return
  // skip middleware on client side entirely
  if (process.client) return
  // or only skip middleware on initial client load
  const nuxtApp = useNuxtApp()
  if (process.client && nuxtApp.isHydrating && nuxtApp.payload.serverRendered) return
})
AdamBD commented 9 months ago

@larbish is there any timeline on this issue? Unfortunately the package as it is now cant really function as an effective auth solution

larbish commented 9 months ago

From what I've understood about the original issue, the auth behaviour was wrong in local. I've checked it and it was related to browser policies concerning cookies on localhost. This trick seems to work for some of you: https://github.com/nuxt-modules/supabase/issues/264#issuecomment-1816349082

@AdamBD if you're facing this issue on production deployment, can you please provide a reproduction so I can check?