如何使用Supabase默认的auth helpers和UI对NextJS和Supabase中的每个页面进行密码保护?

x33g5p2x  于 2023-02-22  发布在  其他
关注(0)|答案(1)|浏览(150)

我正尝试将用户身份验证添加到我的NextJS项目中的每个页面(页面,而不是应用程序)。本教程非常有帮助(也正是我想做的)-https://alexsidorenko.com/blog/next-js-protected-routes/-但我在将Supabase的默认身份验证UI和功能集成到该模型(https://supabase.com/docs/guides/auth/auth-helpers/nextjs)中时遇到了麻烦。
我的基本目标是将身份验证分支转移到_app. tsx中,而不是转移到每个页面上:

// _app.tsx

import { useEffect, useState } from "react";
import { createBrowserSupabaseClient } from '@supabase/auth-helpers-nextjs'
import { SessionContextProvider, useUser, useSession, useSupabaseClient, Session } from '@supabase/auth-helpers-react'
import { Auth, ThemeSupa } from '@supabase/auth-ui-react'
import { AppProps } from 'next/app'
import { UserContext } from "@components/user"

function MyApp({Component, pageProps}: AppProps<{ initialSession: Session }>) {
  const [supabase] = useState(() => createBrowserSupabaseClient())
  const session = useSession()
  const user = useUser()

  console.log("session:" + session);
  console.log("user:" + user);

useEffect(() => {
    if (
    pageProps.protected
  ) {
    return <Auth supabaseClient={supabase} appearance={{ theme: ThemeSupa }} theme="dark" />    
  }
}, [])

  return (
    <SessionContextProvider supabaseClient={supabase} session={session} initialSession={pageProps.initialSession}>
      <Component {...pageProps} />
    </SessionContextProvider>
  )
}
export default MyApp

要保护的页面(例如,索引页)如下所示:

// index.tsx

import Account from "@components/account";

const Home = () => {
  return (
    <div>
          <Account session={session} />
    </div>
  )
}

export async function getStaticProps(context) {
  return {
    props: {
      protected: true,
    },
  }
}

export default Home

索引页面上包含的Account组件是Supabase即用型配置文件面板,但它可以是任何内容:

// @components/account.tsx

import { useState, useEffect } from 'react'

import { useUser, useSupabaseClient, Session } from '@supabase/auth-helpers-react'
import { Database } from '@utils/database.types'
type Profiles = Database['public']['Tables']['profiles']['Row']

export default function Account({ session }: { session: Session }) {
  const supabase = useSupabaseClient<Database>()
  const user = useUser()
  const [loading, setLoading] = useState(true)
  const [username, setUsername] = useState<Profiles['username']>(null)

  useEffect(() => {
    getProfile()
  }, [session])

  async function getProfile() {
    try {
      setLoading(true)
      if (!user) throw new Error('No user')

      let { data, error, status } = await supabase
        .from('profiles')
        .select(`username`)
        .eq('id', user.id)
        .single()

      if (error && status !== 406) {
        throw error
      }

      if (data) {
        setUsername(data.username)
      }
    } catch (error) {
      alert('Error loading user data!')
      console.log(error)
    } finally {
      setLoading(false)
    }
  }

  async function updateProfile({
    username,    
  }: {
    username: Profiles['username']    
  }) {
    try {
      setLoading(true)
      if (!user) throw new Error('No user')

      const updates = {
        id: user.id,
        username,
        updated_at: new Date().toISOString(),
      }

      let { error } = await supabase.from('profiles').upsert(updates)
      if (error) throw error
      alert('Profile updated!')
    } catch (error) {
      alert('Error updating the data!')
      console.log(error)
    } finally {
      setLoading(false)
    }
  }

  return (
    <div>
      <div>
        <label htmlFor="email">Email</label>
        <input id="email" type="text" value={session.user.email} disabled />
      </div>
      <div>
        <label htmlFor="username">Username</label>
        <input id="username" type="text" value={username || ''} onChange={(e) => setUsername(e.target.value)} />
      </div>      
      <div>
        <button onClick={() => updateProfile({ username })} disabled={loading} >
          {loading ? 'Loading ...' : 'Update'}
        </button>
      </div>
      <div>
        <button onClick={() => supabase.auth.signOut()}>
          Sign Out
        </button>
      </div>
    </div>
  )
}

我想我对protected routesSupabase对session和user的使用之间的关系有一个根本性的误解。
任何帮助都将不胜感激。

v440hwme

v440hwme1#

我建议使用Next.js中间件来实现这一点:https://supabase.com/docs/guides/auth/auth-helpers/nextjs#auth-with-nextjs-middleware

import { createMiddlewareSupabaseClient } from '@supabase/auth-helpers-nextjs'
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export async function middleware(req: NextRequest) {
  // We need to create a response and hand it to the supabase client to be able to modify the response headers.
  const res = NextResponse.next()
  // Create authenticated Supabase Client.
  const supabase = createMiddlewareSupabaseClient({ req, res })
  // Check if we have a session
  const {
    data: { session },
  } = await supabase.auth.getSession()

  // Check auth condition
  if (session?.user.email?.endsWith('@gmail.com')) {
    // Authentication successful, forward request to protected route.
    return res
  }

  // Auth condition not met, redirect to home page.
  const redirectUrl = req.nextUrl.clone()
  redirectUrl.pathname = '/'
  redirectUrl.searchParams.set(`redirectedFrom`, req.nextUrl.pathname)
  return NextResponse.redirect(redirectUrl)
}

export const config = {
  matcher: '/middleware-protected/:path*',
}

相关问题