使用Next.js进行Supabase认证
该子模块为在Next中实现用户身份验证提供了方便的帮助Next.js应用程序。
安装Next.js助手库#
1npm install @supabase/auth-helpers-nextjs
此库支持以下工具版本:
- Node.js:
^10.13.0 || >=12.0.0
- Next.js:
>=10
- 注:Next.js13除了新的
app
目录方法外,其他都受支持。我们正在努力增加对此的支持,您可以关注此处.
此外,为可在所有基于React的框架中使用的组件和挂钩安装React Auth Helpers。
1npm install @supabase/auth-helpers-react
设置环境变量
在项目的API设置中检索项目URL和匿名密钥以设置以下环境变量。对于本地开发,可以在.env.local
文件。参见示例。
.env.localNEXT_PUBLIC_SUPABASE_URL=YOUR_SUPABASE_URL NEXT_PUBLIC_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY
基本设置
包装pages/_app.js
组件与SessionContextProvider
组件:
pages/_app.js1import { createBrowserSupabaseClient } from '@supabase/auth-helpers-nextjs'
2import { SessionContextProvider } from '@supabase/auth-helpers-react'
3
4function MyApp({ Component, pageProps }) {
5 const router = useRouter()
6 // Create a new supabase browser client on every first render.
7 const [supabaseClient] = useState(() => createBrowserSupabaseClient())
8
9 return (
10 <SessionContextProvider
11 supabaseClient={supabaseClient}
12 initialSession={pageProps.initialSession}
13 >
14 <Component {...pageProps} />
15 </SessionContextProvider>
16 )
17}
现在,您可以通过检查 useUser()
钩子返回的user
对象是否已定义来确定用户是否已通过身份验证。
使用TypeScript#
您可以将使用Supabase CLI生成的类型传递给Supabase客户端,以获得增强的类型安全性和自动完成:
浏览器客户端
创建新的Supabase数据库客户端对象:
1import { createBrowserSupabaseClient } from '@supabase/auth-helpers-nextjs' 2import { Database } from '../database.types' 3 4const supabaseClient = createBrowserSupabaseClient<Database>()
从SessionContext中检索超级客户端对象:
1import { useSupabaseClient } from '@supabase/auth-helpers-react' 2import { Database } from '../database.types' 3 4const supabaseClient = useSupabaseClient<Database>()
服务器客户端
1// Creating a new supabase server client object (e.g. in API route):
2import type { NextApiRequest, NextApiResponse } from 'next'
3import type { Database } from 'types_db'
4
5export default async (req: NextApiRequest, res: NextApiResponse) => {
6 const supabaseServerClient = createServerSupabaseClient<Database>({
7 req,
8 res,
9 })
10 const {
11 data: { user },
12 } = await supabaseServerClient.auth.getUser()
13
14 res.status(200).json({ name: user?.name ?? '' })
15}
使用RLS获取客户端数据#
为了使行级别安全在客户端获取数据时正常工作,您需要确保使用supabaseClient
钩子中的 useSupabaseClient
,并且仅在 useUser()
钩子中定义了客户端用户后才运行查询:
1import { Auth, ThemeSupa } from '@supabase/auth-ui-react'
2import { useUser, useSupabaseClient } from '@supabase/auth-helpers-react'
3import { useEffect, useState } from 'react'
4
5const LoginPage = () => {
6 const supabaseClient = useSupabaseClient()
7 const user = useUser()
8 const [data, setData] = useState()
9
10 useEffect(() => {
11 async function loadData() {
12 const { data } = await supabaseClient.from('test').select('*')
13 setData(data)
14 }
15 // Only run query once user is logged in.
16 if (user) loadData()
17 }, [user])
18
19 if (!user)
20 return (
21 <Auth
22 redirectTo="http://localhost:3000/"
23 appearance={{ theme: ThemeSupa }}
24 supabaseClient={supabaseClient}
25 providers={['google', 'github']}
26 socialLayout="horizontal"
27 />
28 )
29
30 return (
31 <>
32 <button onClick={() => supabaseClient.auth.signOut()}>Sign out</button>
33 <p>user:</p>
34 <pre>{JSON.stringify(user, null, 2)}</pre>
35 <p>client-side data fetching with RLS</p>
36 <pre>{JSON.stringify(data, null, 2)}</pre>
37 </>
38 )
39}
40
41export default LoginPage
服务器端渲染 (SSR)#
创建服务器超级客户端以检索登录用户的会话:
pages/profile.js1import { createServerSupabaseClient } from '@supabase/auth-helpers-nextjs'
2
3export default function Profile({ user }) {
4 return <div>Hello {user.name}</div>
5}
6
7export const getServerSideProps = async (ctx) => {
8 // Create authenticated Supabase Client
9 const supabase = createServerSupabaseClient(ctx)
10 // Check if we have a session
11 const {
12 data: { session },
13 } = await supabase.auth.getSession()
14
15 if (!session)
16 return {
17 redirect: {
18 destination: '/',
19 permanent: false,
20 },
21 }
22
23 return {
24 props: {
25 initialSession: session,
26 user: session.user,
27 },
28 }
29}
使用RLS获取服务器端数据#
您可以使用服务器超级数据库客户端运行行级别安全性服务器端验证查询:
1import { createServerSupabaseClient } from '@supabase/auth-helpers-nextjs'
2
3export default function ProtectedPage({ user, data }) {
4 return (
5 <>
6 <div>Protected content for {user.email}</div>
7 <pre>{JSON.stringify(data, null, 2)}</pre>
8 <pre>{JSON.stringify(user, null, 2)}</pre>
9 </>
10 )
11}
12
13export const getServerSideProps = async (ctx) => {
14 // Create authenticated Supabase Client
15 const supabase = createServerSupabaseClient(ctx)
16 // Check if we have a session
17 const {
18 data: { session },
19 } = await supabase.auth.getSession()
20
21 if (!session)
22 return {
23 redirect: {
24 destination: '/',
25 permanent: false,
26 },
27 }
28
29 // Run queries with RLS on the server
30 const { data } = await supabase.from('users').select('*')
31
32 return {
33 props: {
34 initialSession: session,
35 user: session.user,
36 data: data ?? [],
37 },
38 }
39}
使用 provider token
将服务器端数据提取到OAuth API {#oauth-provider-token}#
当使用第三方身份验证提供程序时,会话将使用附加的 provider_token
字段启动,该字段保存在身份验证cookie中,可以在会话对象中访问。provider_token
可用于代表登录用户向OAuth提供程序的API端点发出API请求。
1import { createServerSupabaseClient } from '@supabase/auth-helpers-nextjs'
2
3export default function ProtectedPage({ user, allRepos }) {
4 return (
5 <>
6 <div>Protected content for {user.email}</div>
7 <p>Data fetched with provider token:</p>
8 <pre>{JSON.stringify(allRepos, null, 2)}</pre>
9 <p>user:</p>
10 <pre>{JSON.stringify(user, null, 2)}</pre>
11 </>
12 )
13}
14
15export const getServerSideProps = async (ctx) => {
16 // Create authenticated Supabase Client
17 const supabase = createServerSupabaseClient(ctx)
18 // Check if we have a session
19 const {
20 data: { session },
21 } = await supabase.auth.getSession()
22
23 if (!session)
24 return {
25 redirect: {
26 destination: '/',
27 permanent: false,
28 },
29 }
30
31 // Retrieve provider_token & logged in user's third-party id from metadata
32 const { provider_token, user } = session
33 const userId = user.user_metadata.user_name
34
35 const allRepos = await (
36 await fetch(`https://api.github.com/search/repositories?q=user:${userId}`, {
37 method: 'GET',
38 headers: {
39 Authorization: `token ${provider_token}`,
40 },
41 })
42 ).json()
43
44 return { props: { user, allRepos } }
45}
保护API路由#
创建服务器超级客户端以检索登录用户的会话:
pages/api/protected-route.js1import { createServerSupabaseClient } from '@supabase/auth-helpers-nextjs'
2
3const ProtectedRoute = async (req, res) => {
4 // Create authenticated Supabase Client
5 const supabase = createServerSupabaseClient({ req, res })
6 // Check if we have a session
7 const {
8 data: { session },
9 } = await supabase.auth.getSession()
10
11 if (!session)
12 return res.status(401).json({
13 error: 'not_authenticated',
14 description: 'The user does not have an active session or is not authenticated',
15 })
16
17 // Run queries with RLS on the server
18 const { data } = await supabase.from('test').select('*')
19 res.json(data)
20}
21
22export default ProtectedRoute
使用Next.js中间件进行身份验证。#
作为保护单个页面的替代方案,您可以使用Next.js中间件以保护整个目录或与配置对象匹配的目录。在以下示例中,所有对/middleware protected/*
的请求都将检查用户是否已登录,如果成功,请求将被转发到目标路由,否则用户将被重定向:
middleware.ts1import { createMiddlewareSupabaseClient } from '@supabase/auth-helpers-nextjs'
2import { NextResponse } from 'next/server'
3import type { NextRequest } from 'next/server'
4
5export async function middleware(req: NextRequest) {
6 // We need to create a response and hand it to the supabase client to be able to modify the response headers.
7 const res = NextResponse.next()
8 // Create authenticated Supabase Client.
9 const supabase = createMiddlewareSupabaseClient({ req, res })
10 // Check if we have a session
11 const {
12 data: { session },
13 } = await supabase.auth.getSession()
14
15 // Check auth condition
16 if (session?.user.email?.endsWith('@gmail.com')) {
17 // Authentication successful, forward request to protected route.
18 return res
19 }
20
21 // Auth condition not met, redirect to home page.
22 const redirectUrl = req.nextUrl.clone()
23 redirectUrl.pathname = '/'
24 redirectUrl.searchParams.set(`redirectedFrom`, req.nextUrl.pathname)
25 return NextResponse.redirect(redirectUrl)
26}
27
28export const config = {
29 matcher: '/middleware-protected',
30}
迁移指南
迁移到v0.5.X#
使这些助手更灵活,更易于维护,更易于为新版本的Next.js升级,我们将其分解为最有用的部分,即管理cookie,并在任何环境(客户端、服务器、中间件/边缘)中为您提供经过认证的超级js客户端。
因此,我们将withApiAuth
、withPageAuth
和 withMiddlewareAuth
高阶函数标记为已弃用,它们将在下一个次要版本(v0.6.X)中删除。
请按照以下步骤更新API路由、页面和中间件处理程序。谢谢
withApiAuth
已弃用!
在NextApiHandler
中使用createServerSubaseClient
:
pages/api/protected-route.ts1import { withApiAuth } from '@supabase/auth-helpers-nextjs'
2
3export default withApiAuth(async function ProtectedRoute(req, res, supabase) {
4 // Run queries with RLS on the server
5 const { data } = await supabase.from('test').select('*')
6 res.json(data)
7})
withPageAuth
已弃用!
在getServerSideProps
中使用createServerSubaseClient
:
pages/profile.tsx1import { withPageAuth, User } from '@supabase/auth-helpers-nextjs'
2
3export default function Profile({ user }: { user: User }) {
4 return <pre>{JSON.stringify(user, null, 2)}</pre>
5}
6
7export const getServerSideProps = withPageAuth({ redirectTo: '/' })
withMiddlewareAuth
已弃用!
middleware.ts1import { withMiddlewareAuth } from '@supabase/auth-helpers-nextjs' 2 3export const middleware = withMiddlewareAuth({ 4 redirectTo: '/', 5 authGuard: { 6 isPermitted: async (user) => { 7 return user.email?.endsWith('@gmail.com') ?? false 8 }, 9 redirectTo: '/insufficient-permissions', 10 }, 11}) 12 13export const config = { 14 matcher: '/middleware-protected', 15}
迁移到v0.4.X和supabase js v2#
随着supabase-js
v2的更新,不再需要 auth
API路由,因此您可以继续删除 /pages/api/
目录下的 auth
目录。请参阅v2迁移指南以了解supabase js中的全部更改。
/api/auth/logout
api路由已删除,请改用 signout
方法:
1<button 2 onClick={async () => { 3 await supabaseClient.auth.signOut() 4 router.push('/') 5 }} 6> 7 Logout 8</button>
已删除supabaseClient
和 supabaseServerClient
,改用createBrowserSupbaseClient
或createServerSubaseClient
方法。这允许您向客户端提供CLI生成的类型:
1// client-side
2import type { Database } from 'types_db'
3const [supabaseClient] = useState(() => createBrowserSupabaseClient<Database>())
4
5// server-side API route
6import type { NextApiRequest, NextApiResponse } from 'next'
7import type { Database } from 'types_db'
8
9export default async (req: NextApiRequest, res: NextApiResponse) => {
10 const supabaseServerClient = createServerSupabaseClient<Database>({
11 req,
12 res,
13 })
14 const {
15 data: { user },
16 } = await supabaseServerClient.auth.getUser()
17
18 res.status(200).json({ name: user?.name ?? '' })
19}
UserProvider
已被SessionContextProvider
替换。确保包装您的pages/_app.js
与SessionContextProvider
兼容。然后,在整个应用程序中,您可以使用useSessionContext
钩子获取session
,使用useSubaseClient
钩子获取经过身份验证的supbaseClient
。useUser
钩子现在返回user
对象或null
。- TypeScript的用法:您可以将使用Suabase CLI生成的类型传递给Supabase客户端,以获得增强的类型安全性和自动完成:
创建新的超级数据库客户端对象:
1import { Database } from '../database.types'
2
3const [supabaseClient] = useState(() => createBrowserSupabaseClient<Database>())
从SessionContext中检索超级客户端对象:
1import { useSupabaseClient } from '@supabase/auth-helpers-react' 2import { Database } from '../database.types' 3 4const supabaseClient = useSupabaseClient<Database>()