Reputation: 6316
I'm using NextJS 14 (App Router) and Supabase Auth (@supabase/ssr
)
The authentication is working all perfectly fine, but I have parts of my UI that I would like to update when the user is authenticated. For example, I have a header.tsx
component with a top bar that should show "Log In" button when the user is not authenticated and a "Log out" one when the user is. However, the state is not changing automatically when the user authenticates. If I refresh the window it works well.
I've seen there is a listener supabase.auth.onAuthStateChange
that you can use to listen to auth state changes but it doesn't seem to work for me.
This is my header.tsx
component:
'use client'
import { useEffect, useState } from 'react';
import { useRouter } from 'next/navigation';
import { createClient } from '@/utils/supabase/client'
export default function Header() {
const router = useRouter()
const supabase = createClient()
const [userIsLogged, setuserIsLogged] = useState<boolean>(false)
useEffect(() => {
supabase.auth.onAuthStateChange((event, session) => {
setTimeout(async () => {
console.log('onAuthStateChange', event, session)
if (event === 'SIGNED_IN') {
setuserIsLogged(true)
} else if (event === 'SIGNED_OUT') {
setuserIsLogged(false)
}
}, 0)
})
const loadSession = async () => {
const { data: { session }, error } = await supabase.auth.getSession()
if (error) {
console.error(error)
}
if (session) {
setuserIsLogged(true)
}
}
loadSession()
}, [])
return (
<div>
{userIsLogged ? (
<form action="/auth/signout" method="post">
<button type="submit">Sign Out</button>
</form>
) : (
<button onClick={() => router.push('/auth')}>Log in</button>
)}
</div>
);
}
I have also tried returning the unsubscribe
of the auth listener as I've seen recommended somewhere, something like:
const { data } = supabase.auth.onAuthStateChange((event, session) => {
console.log(session?.user);
});
return () => {
data.subscription.unsubscribe()
}
But it doesn't work for me either.
If any other code is needed I can show it, but I don't want this question to be very overwhelming with a lot of code that is not strictly related to the question. I'm happy to edit the question showing more code if needed :)
Upvotes: 6
Views: 1460
Reputation: 21
I was running into this same problem.
I was using React's context api to store app profile info. I wanted it to fetch the profile data when a user logged in and share that info throughout the app. The onAuthStateChanged listener in a useEffect hook would trigger whenever I changed tabs or refreshed the page, but not when a user signed in.
My work-around was to turn my signup form into a client component and handle the submissions and login functions through a browser client instead of an ssr client. When I changed this, the onAuthStateChanged listener caught the sign in event and updated appropriately. It added a few more errors to refactor, such as redirecting with useRouter instead of redirect(), but it seems to work better than any other solution I've found so far.
Upvotes: 2
Reputation: 1152
Using the @supabase/ssr
package will mean that authentication moves to the server side using a cookie. This means that onAuthStateChange
will not work, as it's a client-side mechanism.
I'd recommend that you take a look at the Next.js Supabase Starter Template (https://github.com/vercel/next.js/blob/canary/examples/with-supabase/app/login/page.tsx#L28) there you redirect the user after successful login:
const signIn = async (formData: FormData) => {
"use server";
const email = formData.get("email") as string;
const password = formData.get("password") as string;
const supabase = createClient();
const { error } = await supabase.auth.signInWithPassword({
email,
password,
});
if (error) {
return redirect("/login?message=Could not authenticate user");
}
return redirect("/protected");
};
Upvotes: 2