From 579357089ba1be1ff999bfa00dc1a2f2a49fb1a3 Mon Sep 17 00:00:00 2001 From: Chris Chinchilla Date: Mon, 9 Mar 2026 10:26:16 +0100 Subject: [PATCH] docs: Update auth-helpers usage from examples (#43345) ## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES --- .../lib/supabase-context.tsx | 34 ++++++++++ .../nextjs-auth-presence/package.json | 5 +- .../nextjs-auth-presence/pages/_app.tsx | 31 ++++------ .../nextjs-auth-presence/pages/index.tsx | 43 +++++++++---- .../nextjs-auth-presence/pages/login.tsx | 2 +- .../nextjs-auth-presence/pages/profile.tsx | 42 ++++++++++--- .../app/protected/page.tsx | 5 +- .../nextjs-authorization-demo/package.json | 1 - .../utils/supabase/proxy.ts | 62 ++++--------------- .../utils/supabase/server.ts | 25 +++----- .../nextjs-todo-list/components/TodoList.tsx | 7 +-- .../nextjs-todo-list/lib/initSupabase.ts | 8 +-- .../todo-list/nextjs-todo-list/package.json | 4 +- .../todo-list/nextjs-todo-list/pages/_app.tsx | 8 +-- .../nextjs-todo-list/pages/index.tsx | 17 ++++- .../nextjs-user-management/README.md | 8 +-- 16 files changed, 166 insertions(+), 136 deletions(-) create mode 100644 examples/realtime/nextjs-auth-presence/lib/supabase-context.tsx diff --git a/examples/realtime/nextjs-auth-presence/lib/supabase-context.tsx b/examples/realtime/nextjs-auth-presence/lib/supabase-context.tsx new file mode 100644 index 00000000000..00970d64881 --- /dev/null +++ b/examples/realtime/nextjs-auth-presence/lib/supabase-context.tsx @@ -0,0 +1,34 @@ +import { createBrowserClient } from '@supabase/ssr' +import { User } from '@supabase/supabase-js' +import { createContext, useContext, useEffect, useState } from 'react' + +const supabase = createBrowserClient( + process.env.NEXT_PUBLIC_SUPABASE_URL!, + process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY! +) + +type SupabaseContextType = { + supabase: typeof supabase + user: User | null +} + +const SupabaseContext = createContext({ supabase, user: null }) + +export function SupabaseProvider({ children }: { children: React.ReactNode }) { + const [user, setUser] = useState(null) + + useEffect(() => { + supabase.auth.getUser().then(({ data: { user } }) => setUser(user)) + + const { + data: { subscription }, + } = supabase.auth.onAuthStateChange((_event, session) => setUser(session?.user ?? null)) + + return () => subscription.unsubscribe() + }, []) + + return {children} +} + +export const useSupabaseClient = () => useContext(SupabaseContext).supabase +export const useUser = () => useContext(SupabaseContext).user diff --git a/examples/realtime/nextjs-auth-presence/package.json b/examples/realtime/nextjs-auth-presence/package.json index e2bae23ed1a..a5c1ab77d72 100644 --- a/examples/realtime/nextjs-auth-presence/package.json +++ b/examples/realtime/nextjs-auth-presence/package.json @@ -10,9 +10,8 @@ "format": "prettier --cache --write \"pages/**/*.{js,jsx,ts,tsx,css,md,json}\"" }, "dependencies": { - "@supabase/auth-helpers-nextjs": "^0.6.0", - "@supabase/auth-helpers-react": "^0.3.1", - "@supabase/auth-ui-react": "^0.2.7", + "@supabase/auth-ui-react": "^0.4.7", + "@supabase/ssr": "latest", "@supabase/supabase-js": "^2.7.1", "next": "^13.1.6", "react": "^18.2.0", diff --git a/examples/realtime/nextjs-auth-presence/pages/_app.tsx b/examples/realtime/nextjs-auth-presence/pages/_app.tsx index 188125321b5..26b0b49178b 100644 --- a/examples/realtime/nextjs-auth-presence/pages/_app.tsx +++ b/examples/realtime/nextjs-auth-presence/pages/_app.tsx @@ -1,23 +1,17 @@ -import { createBrowserSupabaseClient, Session } from '@supabase/auth-helpers-nextjs' -import { SessionContextProvider } from '@supabase/auth-helpers-react' +import { SupabaseProvider, useSupabaseClient } from '@/lib/supabase-context' import type { AppProps } from 'next/app' import { useRouter } from 'next/router' -import { useEffect, useState } from 'react' +import { useEffect } from 'react' import '../styles/globals.css' -function MyApp({ - Component, - pageProps, -}: AppProps<{ - initialSession: Session -}>) { +function MyAppInner({ Component, pageProps }: AppProps) { const router = useRouter() - const [supabaseClient] = useState(() => createBrowserSupabaseClient()) + const supabase = useSupabaseClient() useEffect(() => { const { data: { subscription }, - } = supabaseClient.auth.onAuthStateChange((event, session) => { + } = supabase.auth.onAuthStateChange((event) => { switch (event) { case 'SIGNED_IN': router.push('/') @@ -28,15 +22,16 @@ function MyApp({ } }) return subscription.unsubscribe - }, []) + }, [supabase, router]) + return +} + +function MyApp(props: AppProps) { return ( - - - + + + ) } diff --git a/examples/realtime/nextjs-auth-presence/pages/index.tsx b/examples/realtime/nextjs-auth-presence/pages/index.tsx index b382f523f70..108aa72af77 100644 --- a/examples/realtime/nextjs-auth-presence/pages/index.tsx +++ b/examples/realtime/nextjs-auth-presence/pages/index.tsx @@ -1,5 +1,6 @@ -import { createServerSupabaseClient } from '@supabase/auth-helpers-nextjs' -import { useSupabaseClient, useUser } from '@supabase/auth-helpers-react' +import { useSupabaseClient, useUser } from '@/lib/supabase-context' +import { createServerClient } from '@supabase/ssr' +import { serialize } from 'cookie' import { RealtimePresenceState } from '@supabase/supabase-js' import type { GetServerSidePropsContext, NextPage } from 'next' import { useEffect, useState } from 'react' @@ -54,14 +55,35 @@ const HomePage: NextPage = () => { } export const getServerSideProps = async (ctx: GetServerSidePropsContext) => { - // Create authenticated Supabase Client - const supabase = createServerSupabaseClient(ctx) - // Check if we have a session - const { - data: { session }, - } = await supabase.auth.getSession() + const supabase = createServerClient( + process.env.NEXT_PUBLIC_SUPABASE_URL!, + process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY!, + { + cookies: { + getAll() { + return Object.entries(ctx.req.cookies).map(([name, value]) => ({ + name, + value: value ?? '', + })) + }, + setAll(cookiesToSet) { + const existing = ctx.res.getHeader('Set-Cookie') ?? [] + ctx.res.setHeader('Set-Cookie', [ + ...(Array.isArray(existing) ? existing : [String(existing)]), + ...cookiesToSet.map(({ name, value, options }) => + serialize(name, value, options) + ), + ]) + }, + }, + } + ) - if (!session) + const { + data: { user }, + } = await supabase.auth.getUser() + + if (!user) return { redirect: { destination: '/login', @@ -71,8 +93,7 @@ export const getServerSideProps = async (ctx: GetServerSidePropsContext) => { return { props: { - initialSession: session, - user: session.user, + user, }, } } diff --git a/examples/realtime/nextjs-auth-presence/pages/login.tsx b/examples/realtime/nextjs-auth-presence/pages/login.tsx index 7863302b23e..c7f088366b0 100644 --- a/examples/realtime/nextjs-auth-presence/pages/login.tsx +++ b/examples/realtime/nextjs-auth-presence/pages/login.tsx @@ -1,4 +1,4 @@ -import { useSupabaseClient, useUser } from '@supabase/auth-helpers-react' +import { useSupabaseClient, useUser } from '@/lib/supabase-context' import { Auth, ThemeSupa } from '@supabase/auth-ui-react' import type { NextPage } from 'next' import styles from '../styles/Home.module.css' diff --git a/examples/realtime/nextjs-auth-presence/pages/profile.tsx b/examples/realtime/nextjs-auth-presence/pages/profile.tsx index f164dcdc2cd..5337676757d 100644 --- a/examples/realtime/nextjs-auth-presence/pages/profile.tsx +++ b/examples/realtime/nextjs-auth-presence/pages/profile.tsx @@ -1,4 +1,6 @@ -import { createServerSupabaseClient, User } from '@supabase/auth-helpers-nextjs' +import { createServerClient } from '@supabase/ssr' +import { serialize } from 'cookie' +import { User } from '@supabase/supabase-js' import { GetServerSidePropsContext } from 'next' import Link from 'next/link' @@ -15,14 +17,35 @@ export default function Profile({ user }: { user: User }) { } export const getServerSideProps = async (ctx: GetServerSidePropsContext) => { - // Create authenticated Supabase Client - const supabase = createServerSupabaseClient(ctx) - // Check if we have a session - const { - data: { session }, - } = await supabase.auth.getSession() + const supabase = createServerClient( + process.env.NEXT_PUBLIC_SUPABASE_URL!, + process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY!, + { + cookies: { + getAll() { + return Object.entries(ctx.req.cookies).map(([name, value]) => ({ + name, + value: value ?? '', + })) + }, + setAll(cookiesToSet) { + const existing = ctx.res.getHeader('Set-Cookie') ?? [] + ctx.res.setHeader('Set-Cookie', [ + ...(Array.isArray(existing) ? existing : [String(existing)]), + ...cookiesToSet.map(({ name, value, options }) => + serialize(name, value, options) + ), + ]) + }, + }, + } + ) - if (!session) + const { + data: { user }, + } = await supabase.auth.getUser() + + if (!user) return { redirect: { destination: '/login', @@ -32,8 +55,7 @@ export const getServerSideProps = async (ctx: GetServerSidePropsContext) => { return { props: { - initialSession: session, - user: session.user, + user, }, } } diff --git a/examples/realtime/nextjs-authorization-demo/app/protected/page.tsx b/examples/realtime/nextjs-authorization-demo/app/protected/page.tsx index 0c497343775..0d08d840d5f 100644 --- a/examples/realtime/nextjs-authorization-demo/app/protected/page.tsx +++ b/examples/realtime/nextjs-authorization-demo/app/protected/page.tsx @@ -1,6 +1,7 @@ 'use client' import CreateRoomModal from '@/components/create-room-modal' -import { User, createClientComponentClient } from '@supabase/auth-helpers-nextjs' +import { createClient } from '@/utils/supabase/client' +import { User } from '@supabase/supabase-js' import { RealtimeChannel } from '@supabase/supabase-js' import { useState, useEffect, use } from 'react' @@ -15,7 +16,7 @@ export default function Chat() { const [showModal, setShowModal] = useState(false) const [error, setError] = useState(null) - const supabase = createClientComponentClient() + const supabase = createClient() const getChannels = async () => { const channels = await supabase.from('rooms').select('topic') diff --git a/examples/realtime/nextjs-authorization-demo/package.json b/examples/realtime/nextjs-authorization-demo/package.json index 904c35f4b46..0446a15d347 100644 --- a/examples/realtime/nextjs-authorization-demo/package.json +++ b/examples/realtime/nextjs-authorization-demo/package.json @@ -9,7 +9,6 @@ "start": "next start" }, "dependencies": { - "@supabase/auth-helpers-nextjs": "^0.9.0", "@supabase/ssr": "latest", "@supabase/supabase-js": "2.44.0", "autoprefixer": "10.4.17", diff --git a/examples/realtime/nextjs-authorization-demo/utils/supabase/proxy.ts b/examples/realtime/nextjs-authorization-demo/utils/supabase/proxy.ts index 66eb55acee4..acb215595e2 100644 --- a/examples/realtime/nextjs-authorization-demo/utils/supabase/proxy.ts +++ b/examples/realtime/nextjs-authorization-demo/utils/supabase/proxy.ts @@ -1,60 +1,26 @@ -import { createServerClient, type CookieOptions } from "@supabase/ssr"; +import { createServerClient } from "@supabase/ssr"; import { type NextRequest, NextResponse } from "next/server"; export const updateSession = async (request: NextRequest) => { // This `try/catch` block is only here for the interactive tutorial. // Feel free to remove once you have Supabase connected. try { - // Create an unmodified response - let response = NextResponse.next({ - request: { - headers: request.headers, - }, - }); + let supabaseResponse = NextResponse.next({ request }); const supabase = createServerClient( process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY!, { cookies: { - get(name: string) { - return request.cookies.get(name)?.value; + getAll() { + return request.cookies.getAll(); }, - set(name: string, value: string, options: CookieOptions) { - // If the cookie is updated, update the cookies for the request and response - request.cookies.set({ - name, - value, - ...options, - }); - response = NextResponse.next({ - request: { - headers: request.headers, - }, - }); - response.cookies.set({ - name, - value, - ...options, - }); - }, - remove(name: string, options: CookieOptions) { - // If the cookie is removed, update the cookies for the request and response - request.cookies.set({ - name, - value: "", - ...options, - }); - response = NextResponse.next({ - request: { - headers: request.headers, - }, - }); - response.cookies.set({ - name, - value: "", - ...options, - }); + setAll(cookiesToSet) { + cookiesToSet.forEach(({ name, value }) => request.cookies.set(name, value)); + supabaseResponse = NextResponse.next({ request }); + cookiesToSet.forEach(({ name, value, options }) => + supabaseResponse.cookies.set(name, value, options) + ); }, }, }, @@ -64,15 +30,11 @@ export const updateSession = async (request: NextRequest) => { // https://supabase.com/docs/guides/auth/server-side/nextjs await supabase.auth.getUser(); - return response; + return supabaseResponse; } catch (e) { // If you are here, a Supabase client could not be created! // This is likely because you have not set up environment variables. // Check out http://localhost:3000 for Next Steps. - return NextResponse.next({ - request: { - headers: request.headers, - }, - }); + return NextResponse.next({ request }); } }; diff --git a/examples/realtime/nextjs-authorization-demo/utils/supabase/server.ts b/examples/realtime/nextjs-authorization-demo/utils/supabase/server.ts index 3f308a23f5f..cf32b1dfdc8 100644 --- a/examples/realtime/nextjs-authorization-demo/utils/supabase/server.ts +++ b/examples/realtime/nextjs-authorization-demo/utils/supabase/server.ts @@ -1,4 +1,4 @@ -import { createServerClient, type CookieOptions } from '@supabase/ssr' +import { createServerClient } from '@supabase/ssr' import { cookies } from 'next/headers' export const createClient = async () => { @@ -9,23 +9,16 @@ export const createClient = async () => { process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY!, { cookies: { - get(name: string) { - return cookieStore.get(name)?.value + getAll() { + return cookieStore.getAll() }, - set(name: string, value: string, options: CookieOptions) { + setAll(cookiesToSet) { try { - cookieStore.set({ name, value, ...options }) - } catch (error) { - // The `set` method was called from a Server Component. - // This can be ignored if you have proxy refreshing - // user sessions. - } - }, - remove(name: string, options: CookieOptions) { - try { - cookieStore.set({ name, value: '', ...options }) - } catch (error) { - // The `delete` method was called from a Server Component. + cookiesToSet.forEach(({ name, value, options }) => + cookieStore.set(name, value, options) + ) + } catch { + // The `setAll` method was called from a Server Component. // This can be ignored if you have proxy refreshing // user sessions. } diff --git a/examples/todo-list/nextjs-todo-list/components/TodoList.tsx b/examples/todo-list/nextjs-todo-list/components/TodoList.tsx index 0bc90ade4ab..a6375a243d6 100644 --- a/examples/todo-list/nextjs-todo-list/components/TodoList.tsx +++ b/examples/todo-list/nextjs-todo-list/components/TodoList.tsx @@ -1,11 +1,11 @@ import { Database } from '@/lib/schema' -import { Session, useSupabaseClient } from '@supabase/auth-helpers-react' +import { supabase } from '@/lib/initSupabase' +import { Session } from '@supabase/supabase-js' import { useEffect, useState } from 'react' type Todos = Database['public']['Tables']['todos']['Row'] export default function TodoList({ session }: { session: Session }) { - const supabase = useSupabaseClient() const [todos, setTodos] = useState([]) const [newTaskText, setNewTaskText] = useState('') const [errorText, setErrorText] = useState('') @@ -24,7 +24,7 @@ export default function TodoList({ session }: { session: Session }) { } fetchTodos() - }, [supabase]) + }, []) const addTodo = async (taskText: string) => { let task = taskText.trim() @@ -90,7 +90,6 @@ export default function TodoList({ session }: { session: Session }) { } const Todo = ({ todo, onDelete }: { todo: Todos; onDelete: () => void }) => { - const supabase = useSupabaseClient() const [isCompleted, setIsCompleted] = useState(todo.is_complete) const toggle = async () => { diff --git a/examples/todo-list/nextjs-todo-list/lib/initSupabase.ts b/examples/todo-list/nextjs-todo-list/lib/initSupabase.ts index 0895fb27dd5..75f24396382 100644 --- a/examples/todo-list/nextjs-todo-list/lib/initSupabase.ts +++ b/examples/todo-list/nextjs-todo-list/lib/initSupabase.ts @@ -1,6 +1,6 @@ -import { createClient } from '@supabase/supabase-js' +import { createBrowserClient } from '@supabase/ssr' -export const supabase = createClient( - process.env.NEXT_PUBLIC_SUPABASE_URL ?? '', - process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY ?? '' +export const supabase = createBrowserClient( + process.env.NEXT_PUBLIC_SUPABASE_URL!, + process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY! ) diff --git a/examples/todo-list/nextjs-todo-list/package.json b/examples/todo-list/nextjs-todo-list/package.json index 32fac003794..23eb0d07e0a 100644 --- a/examples/todo-list/nextjs-todo-list/package.json +++ b/examples/todo-list/nextjs-todo-list/package.json @@ -12,8 +12,8 @@ }, "dependencies": { "@next/font": "13.1.6", - "@supabase/auth-helpers-react": "^0.3.1", - "@supabase/auth-ui-react": "^0.2.8", + "@supabase/auth-ui-react": "^0.4.7", + "@supabase/ssr": "latest", "@supabase/supabase-js": "^2.8.0", "@types/node": "18.14.0", "@types/react": "18.0.28", diff --git a/examples/todo-list/nextjs-todo-list/pages/_app.tsx b/examples/todo-list/nextjs-todo-list/pages/_app.tsx index 59d31d438bc..e2cad625217 100644 --- a/examples/todo-list/nextjs-todo-list/pages/_app.tsx +++ b/examples/todo-list/nextjs-todo-list/pages/_app.tsx @@ -1,12 +1,6 @@ -import { supabase } from '@/lib/initSupabase' import '@/styles/app.css' -import { SessionContextProvider } from '@supabase/auth-helpers-react' import type { AppProps } from 'next/app' export default function App({ Component, pageProps }: AppProps) { - return ( - - - - ) + return } diff --git a/examples/todo-list/nextjs-todo-list/pages/index.tsx b/examples/todo-list/nextjs-todo-list/pages/index.tsx index 7c700d67c33..48f34bc54a1 100644 --- a/examples/todo-list/nextjs-todo-list/pages/index.tsx +++ b/examples/todo-list/nextjs-todo-list/pages/index.tsx @@ -1,11 +1,22 @@ import Head from 'next/head' -import { useSession, useSupabaseClient } from '@supabase/auth-helpers-react' +import { supabase } from '@/lib/initSupabase' import { Auth, ThemeSupa } from '@supabase/auth-ui-react' import TodoList from '@/components/TodoList' +import { Session } from '@supabase/supabase-js' +import { useEffect, useState } from 'react' export default function Home() { - const session = useSession() - const supabase = useSupabaseClient() + const [session, setSession] = useState(null) + + useEffect(() => { + supabase.auth.getSession().then(({ data: { session } }) => setSession(session)) + + const { + data: { subscription }, + } = supabase.auth.onAuthStateChange((_event, session) => setSession(session)) + + return () => subscription.unsubscribe() + }, []) return ( <> diff --git a/examples/user-management/nextjs-user-management/README.md b/examples/user-management/nextjs-user-management/README.md index 49e2dd72d3f..54b478808c8 100644 --- a/examples/user-management/nextjs-user-management/README.md +++ b/examples/user-management/nextjs-user-management/README.md @@ -5,8 +5,8 @@ This example will set you up for a very common situation: users can sign up or s This demonstrates how to use: - User signups using Supabase [Auth](https://supabase.com/auth). - - Supabase [Auth Helpers for Next.js](https://supabase.com/docs/guides/auth/auth-helpers/nextjs). - - Supabase [pre-built Auth UI for React](https://supabase.com/docs/guides/auth/auth-helpers/auth-ui). + - Supabase [SSR Auth for Next.js](https://supabase.com/docs/guides/auth/server-side/nextjs). + - Supabase [pre-built Auth UI for React](https://supabase.com/docs/guides/auth/auth-ui). - User avatar images using Supabase [Storage](https://supabase.com/storage) - Public profiles restricted with [Policies](https://supabase.com/docs/guides/auth#policies). - Frontend using [Next.js](<[nextjs.org/](https://nextjs.org/)>). @@ -16,8 +16,8 @@ This demonstrates how to use: - Frontend: - [Next.js](https://github.com/vercel/next.js) - a React framework for production. - [Supabase.js](https://supabase.com/docs/library/getting-started) for user management and realtime data syncing. - - Supabase [Auth Helpers for Next.js](https://supabase.com/docs/guides/auth/auth-helpers/nextjs). - - Supabase [pre-built Auth UI for React](https://supabase.com/docs/guides/auth/auth-helpers/auth-ui). + - Supabase [`@supabase/ssr`](https://supabase.com/docs/guides/auth/server-side/nextjs) for SSR auth. + - Supabase [pre-built Auth UI for React](https://supabase.com/docs/guides/auth/auth-ui). - Backend: - [supabase.com/dashboard](https://supabase.com/dashboard/): hosted Postgres database with restful API for usage with Supabase.js.