supabase icon indicating copy to clipboard operation
supabase copied to clipboard

Auth not set on server side with link (magic link) login

Open mwohlan opened this issue 2 years ago • 6 comments

Version

@nuxtjs/supabase: v0.1.9 nuxt: v3.0.0-rc.1

Steps to reproduce

Set up a login form using the just the email to receive a magic link

const { session, error } = await client.auth.signIn({ email: email.value })

Use a middleware like this

export default defineNuxtRouteMiddleware((to) => {
  const user = useSupabaseUser()
  if (!(user.value))
    return navigateTo({ name: 'login', query: { redirect: to.path } })
})

What is Expected?

The user should be set on the server side.

What is actually happening?

The token on the server side plugin is null:

export default defineNuxtPlugin(async () => {
  const user = useSupabaseUser();
  const client = useSupabaseClient();
  const token = useSupabaseToken();
  if (!token.value) {
    return;
  }
  const { user: supabaseUser, error } = await client.auth.api.getUser(token.value);
  user.value = error ? null : supabaseUser;
});

Therefore the middleware redirects the user to the login and the user only becomes available after the middleware has run, leaving the user on the login page. A fix would involve watching the user, but thats still leads to the login page shortly showing up before the user is redirected again.

This issue is linked to #28 but singles out the ssr issue when using magic links

mwohlan avatar Apr 25 '22 08:04 mwohlan

I have no choice, the Supabase onAuthStateChange event is sent on client side. Once the event is received, it shares the data between server and client. Until then, the server is not synced. 😞

To avoid the redirect to login page and the display of the login form there are two solutions depending on if you are using providers or magic link.

Solution using providers

Add the redirectTo option in the signIn method:

client.auth.signIn({ email: '[email protected]' }, {
    redirectTo: 'localhost:3000/confirm'
  })

Create you confirm.vue file and listen to user change

<template>
  <div>
    Redirecting...
  </div>
</template>

<script setup>
const user = useSupabaseUser()

watchEffect(() => {
  if (user.value) {
    navigateTo('/')
  }
})
</script>

Don't forget to add your redirect Url in your Supabase dashboard: Screenshot 2022-04-25 at 16 30 05

Solution using magic link

This one is a little bit tricky because the ConfirmationUrl is not override by the redirect one. But you can override the confirmation template and use the confirm.vue file in the exact same way as described in the first solution Screenshot 2022-04-25 at 16 33 11

Hope it helps 🤞

larbish avatar Apr 25 '22 13:04 larbish

Hello @larbish can you please explain what you mean with the magic link solution? A sample codebase for both signup and signin will be great. Will there be a more permanent solution or this is it?

nosizejosh avatar May 04 '22 22:05 nosizejosh

Hey @nosizejosh, that's a way I found to avoid the display of the login form highlited by @mwohlan. That was just a proposition.

larbish avatar May 09 '22 16:05 larbish

@larbish from this tutorial https://vueschool.io/articles/vuejs-tutorials/use-supabase-auth-with-vue-js-3/ and this snippet

// setup auth state listener
supabase.auth.onAuthStateChange((event, session) => {
  // the "event" is a string indicating what trigger the state change (ie. SIGN_IN, SIGN_OUT, etc)
  // the session contains info about the current session most importanly the user dat
  const { user } = useAuthUser();

  // if the user exists in the session we're logged in
  // and we can set our user reactive ref
  user.value = session?.user || null;
});

does this module create an auth state listener to be able to solve this issue? if not, how can I integrate this for example in my codebase so auth state change can be monitored to solve this issue? Thank you

nosizejosh avatar May 17 '22 13:05 nosizejosh

@nosizejosh Please refer to my response. Auth state changes are monitored and synced with server (you can check the code) but the supabase event is fired on client side. You can also read this issue that explains the same server side sync issue.

larbish avatar May 17 '22 14:05 larbish

Thank you @larbish

nosizejosh avatar May 17 '22 16:05 nosizejosh