虽然Supabase因其实时数据库和 API 层而广为人知,但我喜欢它的一件事是它提供了许多易于设置的身份验证机制开箱即用。

Supabase Auth

魔力链接

我的最爱之一是 Magic Link。您过去可能使用过魔术链接。魔术链接通过电子邮件向用户发送一个链接,其中包含通过自定义 URL 和访问令牌对服务进行身份验证的链接。

当用户访问 URL 时,会在他们的浏览器存储中设置一个会话,并将用户重定向回应用程序,在此过程中对用户进行身份验证。

这正在成为一种非常流行的用户身份验证方式,因为他们不必跟上另一个密码,它提供了非常棒的用户体验。

Next.js

使用 Next.js,您不仅可以通过客户端授权保护路由,而且为了增加安全性,您可以在getServerSideProps中进行服务器端授权和重定向(如果已设置 cookie 并且在请求中可用)语境。

这也是 Supabase 派上用场的地方。在 SSR 和 API 路由中为登录用户设置和获取 cookie 的内置功能:

在 API 路由中设置用户

import { supabase } from '../../client'

export default function handler(req, res) {
  supabase.auth.api.setAuthCookie(req, res)
}

在 SSR 或 API 路由中获取用户

export async function getServerSideProps({ req }) {
  const { user } = await supabase.auth.api.getUserByCookie(req)

  if (!user) {
    return {
      props: {} 
    }
  }

  return { props: { user } }
}

从 SEO 的角度来看,服务器端重定向通常比客户端重定向更受欢迎——搜索引擎更难理解应该如何处理客户端重定向。

您还可以使用getUserByCookie函数从 API 路由访问用户配置文件,从而打开一组全新的用例和功能。

使用 Next.js 和 Supabase,您可以使用 SSG、SSR 和客户端数据获取和用户授权的组合轻松实现各种应用程序,使组合(以及提供这种功能组合的任何框架)非常有用,并且强大的。

我们将要构建的内容

在这篇文章中,我们将构建一个 Next.js 应用程序,该应用程序支持导航、身份验证、授权、重定向(客户端和服务器端)和配置文件视图。

我们将要构建的项目对于需要处理用户身份的任何应用程序来说都是一个很好的起点,并且是了解用户身份如何使用现代混合技术在项目中的所有不同位置工作和流动的好方法Next.js 之类的框架。

这个项目的最终代码位于这里

构建应用程序

要开始使用,您首先需要创建一个 Supbase 帐户和项目。

为此,请前往Supabase.io并单击 Start Your Project。使用 GitHub 进行身份验证,然后在您帐户中提供给您的组织下创建一个新项目。

创建 Supabase 账号

为项目指定名称和密码,然后单击创建新项目。

创建您的项目大约需要 2 分钟。

接下来,打开您的终端并创建一个新的 Next.js 应用程序:

npx create-next-app supabase-next-auth

cd supabase-next-auth

我们需要的唯一依赖是@supabase/supabase-js包:

npm install @supabase/supabase-js

配置 Supabase 凭据

现在 Next.js 应用程序已创建,它需要了解 Supbase 项目才能与之交互。

最好的方法是使用环境变量。 Next.js 允许通过在项目的根目录中创建一个名为 .env.local 的文件并将它们存储在那里来设置环境变量。

为了向浏览器公开变量,您必须在变量前面加上 NEXT_PUBLIC_

在项目的根目录下创建一个名为 .env.local 的文件,并添加以下配置:

NEXT_PUBLIC_SUPABASE_URL=https://app-id.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-public-api-key

您可以在 Supabase 仪表板设置中找到您的 API URL 和 API Key 的值:

Supabase 配置

创建 Supabase 客户端

现在已经设置了环境变量,我们可以创建一个 Supbase 实例,可以在需要时导入它。

使用以下代码在项目的根目录中创建一个名为 client.js 的文件:

/* client.js */
import { createClient } from '@supabase/supabase-js'

const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY
)

export { supabase }

更新索引页面

接下来,让我们将 pages/index.js 更新为比现成提供的更简单的内容。这只是作为一个基本的登陆页面

/* pages/index.js */
import styles from '../styles/Home.module.css'
export default function Home() {
  return (
    <div className={styles.container}>
      <main className={styles.main}>
        <h1 className={styles.title}>
          Hello World!
        </h1>
       </main>
    </div>
  )
}

创建登录屏幕

接下来,让我们创建登录屏幕。这将为用户提供一个表单输入,以提供他们的电子邮件地址。

当用户提交表单时,他们将收到一个用于登录的魔术链接。这对新用户和现有用户都有效!

pages 目录中创建一个名为 sign-in.js 的新文件:

/* pages/sign-in.js */
import { useState } from 'react'
import styles from '../styles/Home.module.css'

import { supabase } from '../client'

export default function Home() {
  const [email, setEmail] = useState('')
  const [submitted, setSubmitted] = useState(false)
  async function signIn() {
    const { error, data } = await supabase.auth.signIn({
      email
    })
    if (error) {
      console.log({ error })
    } else {
      setSubmitted(true)
    }
  }
  if (submitted) {
    return (
      <div className={styles.container}>
        <h1>Please check your email to sign in</h1>
      </div>
    )
  }
  return (
    <div className={styles.container}>
      <main className={styles.main}>
        <h1 className={styles.title}>
          Sign In
        </h1>
        <input
          onChange={e => setEmail(e.target.value)}
          style={{ margin: 10 }}
        />
        <button onClick={() => signIn()}>Sign In</button>
       </main>
    </div>
  )
}

这个文件的主要内容是这行代码:

const { error, data } = await supabase.auth.signIn({
  email
})

通过仅提供用户的电子邮件地址,魔术链接身份验证将自动进行。

剖面图

接下来,让我们创建配置文件视图。在 pages 目录中创建一个名为 profile.js 的新文件:

/* pages/profile.js */
import { useState, useEffect } from 'react';
import { supabase } from '../client'
import { useRouter } from 'next/router'

export default function Profile() {
  const [profile, setProfile] = useState(null)
  const router = useRouter()
  useEffect(() => {
    fetchProfile()
  }, [])
  async function fetchProfile() {
    const profileData = await supabase.auth.user()
    if (!profileData) {
      router.push('/sign-in')
    } else {
      setProfile(profileData)
    }
  }
  async function signOut() {
    await supabase.auth.signOut()
    router.push('/sign-in')
  }
  if (!profile) return null
  return (
    <div style={{ maxWidth: '420px', margin: '96px auto' }}>
      <h2>Hello, {profile.email}</h2>
      <p>User ID: {profile.id}</p>
      <button onClick={signOut}>Sign Out</button>
    </div>
  )
}

要检查当前登录的用户,我们调用supabase.auth.user()

如果用户已登录,我们使用setProfile函数设置用户信息,该函数使用useState挂钩设置。

如果用户未登录,我们使用useRouter挂钩进行客户端重定向。

API路由

pages/_app.js 中,我们需要调用一个函数来设置 cookie,以便稍后在 SSR 路由中检索。

让我们继续创建 API 路由和函数。这将调用 Supabase 客户端提供给我们的setAuthCookieAPI。

pages/api 文件夹中创建一个名为 auth.js 的新文件,并添加以下代码:

/* pages/api/auth.js */
import { supabase } from '../../client'

export default function handler(req, res) {
  supabase.auth.api.setAuthCookie(req, res)
}

Nav,auth listener,设置会话cookie

我们需要编写的最大代码块将在 pages/app.js 中。以下是我们需要在这里实现的东西:

1.导航

  1. 身份验证状态更改时触发的侦听器(由 Supbase 提供)

  2. 使用用户会话设置 cookie 的功能

除此之外,我们还需要跟上用户的身份验证状态。我们这样做是为了根据用户是否登录来切换链接、显示或隐藏某些链接。

我们将通过仅向未登录的用户显示 Sign In 链接并在他们登录时隐藏它来演示这一点。

/* pages/_app.js */
import '../styles/globals.css'
import { useState, useEffect } from 'react'
import Link from 'next/link'
import { supabase } from '../client'
import { useRouter } from 'next/router'

function MyApp({ Component, pageProps }) {
  const router = useRouter()
  const [authenticatedState, setAuthenticatedState] = useState('not-authenticated')
  useEffect(() => {
    /* fires when a user signs in or out */
    const { data: authListener } = supabase.auth.onAuthStateChange((event, session) => {
      handleAuthChange(event, session)
      if (event === 'SIGNED_IN') {
        setAuthenticatedState('authenticated')
        router.push('/profile')
      }
      if (event === 'SIGNED_OUT') {
        setAuthenticatedState('not-authenticated')
      }
    })
    checkUser()
    return () => {
      authListener.unsubscribe()
    }
  }, [])
  async function checkUser() {
    /* when the component loads, checks user to show or hide Sign In link */
    const user = await supabase.auth.user()
    if (user) {
      setAuthenticatedState('authenticated')
    }
  }
  async function handleAuthChange(event, session) {
    /* sets and removes the Supabase cookie */
    await fetch('/api/auth', {
      method: 'POST',
      headers: new Headers({ 'Content-Type': 'application/json' }),
      credentials: 'same-origin',
      body: JSON.stringify({ event, session }),
    })
  }
  return (
    <div>
      <nav style={navStyle}>
        <Link href="/">
          <a style={linkStyle}>Home</a>
        </Link>
        <Link href="/profile">
          <a style={linkStyle}>Profile</a>
        </Link>
        {
          authenticatedState === 'not-authenticated' && (
            <Link href="/sign-in">
              <a style={linkStyle}>Sign In</a>
            </Link>
          )
        }
        <Link href="/protected">
          <a style={linkStyle}>Protected</a>
        </Link>
      </nav>
      <Component {...pageProps} />
    </div>
  )
}

const navStyle = {
  margin: 20
}
const linkStyle = {
  marginRight: 10
}

export default MyApp

我们需要实现的最后一页是演示服务器端保护和重定向的路由。

由于我们已经实现了设置 cookie,如果用户登录,我们现在应该能够读取服务器上的 cookie。

就像我之前提到的,我们可以使用getUserByCookie函数来做到这一点。

pages 目录中创建一个名为 protected.js 的新文件,并添加以下代码:

import { supabase } from '../client'

export default function Profile({ user }) {
  console.log({ user })
  return (
    <div style={{ maxWidth: '420px', margin: '96px auto' }}>
      <h2>Hello from protected route</h2>
    </div>
  )
}

export async function getServerSideProps({ req }) {
  /* check to see if a user is set */
  const { user } = await supabase.auth.api.getUserByCookie(req)

  /* if no user is set, redirect to the sign-in page */
  if (!user) {
    return { props: {}, redirect: { destination: '/sign-in' } }
  }

  /* if a user is set, pass it to the page via props */
  return { props: { user } }
}

测试一下

现在应用程序已构建,我们可以对其进行测试!

要运行该应用程序,请打开您的终端并运行以下命令:

npm run dev

当应用程序加载时,您应该能够注册,并使用魔术链接登录。登录后,您应该能够查看个人资料页面并查看您的用户 ID 以及您的电子邮件地址。

设置元数据和属性

如果您想继续构建用户的配置文件,您可以使用update方法轻松完成。

例如,假设我们希望允许用户设置他们的位置。我们可以使用以下代码来做到这一点:

const { user, error } = await supabase.auth.update({ 
  data: {
    city: "New York"
  } 
})

现在,当我们获取用户的数据时,我们应该能够查看他们的元数据:

用户元数据

这个项目的最终代码位于这里

Logo

云原生社区为您提供最前沿的新闻资讯和知识内容

更多推荐