跳至内容
从 NextAuth.js v4 迁移?阅读 我们的迁移指南.

Supabase 适配器

资源

设置

安装

npm install @supabase/supabase-js @auth/supabase-adapter

环境变量

SUPABASE_URL
SUPABASE_SERVICE_ROLE_KEY

配置

./auth.ts
import NextAuth from "next-auth"
import { SupabaseAdapter } from "@auth/supabase-adapter"
 
export const { handlers, auth, signIn, signOut } = NextAuth({
  providers: [],
  adapter: SupabaseAdapter({
    url: process.env.SUPABASE_URL,
    secret: process.env.SUPABASE_SERVICE_ROLE_KEY,
  }),
})
💡

此适配器由社区开发,并非由 Supabase 官方维护或支持。它使用 Supabase 数据库在单独的 next_auth 架构中存储用户和会话数据。它是一个独立的 Auth 服务器,不与 Supabase Auth 交互,因此提供不同的功能集。

如果您正在寻找具有更多功能(例如 内置电子邮件服务器电话验证多因素身份验证 (MFA / 2FA))的官方维护的 Auth 服务器,请使用 Supabase AuthNext.js 的 Auth Helpers

架构

按照我们主 架构 中的描述设置您的数据库,方法是在 Supabase SQL 编辑器 中复制下面的 SQL 架构。

或者,您可以在 SQL 编辑器页面 上选择 NextAuth 快速入门卡,或者 使用 Supabase CLI 创建迁移

--
-- Name: next_auth; Type: SCHEMA;
--
CREATE SCHEMA next_auth;
 
GRANT USAGE ON SCHEMA next_auth TO service_role;
GRANT ALL ON SCHEMA next_auth TO postgres;
 
--
-- Create users table
--
CREATE TABLE IF NOT EXISTS next_auth.users
(
    id uuid NOT NULL DEFAULT uuid_generate_v4(),
    name text,
    email text,
    "emailVerified" timestamp with time zone,
    image text,
    CONSTRAINT users_pkey PRIMARY KEY (id),
    CONSTRAINT email_unique UNIQUE (email)
);
 
GRANT ALL ON TABLE next_auth.users TO postgres;
GRANT ALL ON TABLE next_auth.users TO service_role;
 
--- uid() function to be used in RLS policies
CREATE FUNCTION next_auth.uid() RETURNS uuid
    LANGUAGE sql STABLE
    AS $$
  select
  	coalesce(
		nullif(current_setting('request.jwt.claim.sub', true), ''),
		(nullif(current_setting('request.jwt.claims', true), '')::jsonb ->> 'sub')
	)::uuid
$$;
 
--
-- Create sessions table
--
CREATE TABLE IF NOT EXISTS  next_auth.sessions
(
    id uuid NOT NULL DEFAULT uuid_generate_v4(),
    expires timestamp with time zone NOT NULL,
    "sessionToken" text NOT NULL,
    "userId" uuid,
    CONSTRAINT sessions_pkey PRIMARY KEY (id),
    CONSTRAINT sessionToken_unique UNIQUE ("sessionToken"),
    CONSTRAINT "sessions_userId_fkey" FOREIGN KEY ("userId")
        REFERENCES  next_auth.users (id) MATCH SIMPLE
        ON UPDATE NO ACTION
        ON DELETE CASCADE
);
 
GRANT ALL ON TABLE next_auth.sessions TO postgres;
GRANT ALL ON TABLE next_auth.sessions TO service_role;
 
--
-- Create accounts table
--
CREATE TABLE IF NOT EXISTS  next_auth.accounts
(
    id uuid NOT NULL DEFAULT uuid_generate_v4(),
    type text NOT NULL,
    provider text NOT NULL,
    "providerAccountId" text NOT NULL,
    refresh_token text,
    access_token text,
    expires_at bigint,
    token_type text,
    scope text,
    id_token text,
    session_state text,
    oauth_token_secret text,
    oauth_token text,
    "userId" uuid,
    CONSTRAINT accounts_pkey PRIMARY KEY (id),
    CONSTRAINT provider_unique UNIQUE (provider, "providerAccountId"),
    CONSTRAINT "accounts_userId_fkey" FOREIGN KEY ("userId")
        REFERENCES  next_auth.users (id) MATCH SIMPLE
        ON UPDATE NO ACTION
        ON DELETE CASCADE
);
 
GRANT ALL ON TABLE next_auth.accounts TO postgres;
GRANT ALL ON TABLE next_auth.accounts TO service_role;
 
--
-- Create verification_tokens table
--
CREATE TABLE IF NOT EXISTS  next_auth.verification_tokens
(
    identifier text,
    token text,
    expires timestamp with time zone NOT NULL,
    CONSTRAINT verification_tokens_pkey PRIMARY KEY (token),
    CONSTRAINT token_unique UNIQUE (token),
    CONSTRAINT token_identifier_unique UNIQUE (token, identifier)
);
 
GRANT ALL ON TABLE next_auth.verification_tokens TO postgres;
GRANT ALL ON TABLE next_auth.verification_tokens TO service_role;

在 Supabase 中公开 NextAuth 架构

通过 API 设置 中的无服务器 API 公开 next_auth 架构,方法是将 next_auth 添加到“公开的架构”列表中。

在本地开发时,将 next_auth 添加到 supabase 文件夹中由 Supabase CLI 生成的 config.toml 文件中的 schemas 数组中。

高级用法

启用行级安全 (RLS)

Postgres 提供了一个称为 行级安全 (RLS) 的强大功能,以限制对数据的访问。

这是通过将签名的 JWT 发送到您的 Supabase 无服务器 API 来实现的。让它与 NextAuth 协同工作需要两个步骤。

在会话回调中生成 Supabase access_token JWT

要签署 JWT,请使用 jsonwebtoken 包。

npm install jsonwebtoken

使用 会话回调 创建 Supabase access_token 并将其附加到 session 对象中。

要签署 JWT,请使用 Supabase JWT 密钥,该密钥可以在 API 设置 中找到。

./auth.ts
import NextAuth from "next-auth"
import { SupabaseAdapter } from "@auth/supabase-adapter"
import jwt from "jsonwebtoken"
 
// For more information on each option (and a full list of options) go to
// https://authjs.oauth.ac.cn/reference/core/types#authconfig
export const { handlers, auth, signIn, signOut } = NextAuth({
  // https://authjs.oauth.ac.cn/getting-started/authentication/oauth
  providers: [],
  adapter: SupabaseAdapter({
    url: process.env.NEXT_PUBLIC_SUPABASE_URL,
    secret: process.env.SUPABASE_SERVICE_ROLE_KEY,
  }),
  callbacks: {
    async session({ session, user }) {
      const signingSecret = process.env.SUPABASE_JWT_SECRET
      if (signingSecret) {
        const payload = {
          aud: "authenticated",
          exp: Math.floor(new Date(session.expires).getTime() / 1000),
          sub: user.id,
          email: user.email,
          role: "authenticated",
        }
        session.supabaseAccessToken = jwt.sign(payload, signingSecret)
      }
      return session
    },
  },
})

将 Supabase access_token JWT 注入客户端

例如,假设以下公共架构

-- Note: This table contains user data. Users should only be able to view and update their own data.
create table users (
  -- UUID from next_auth.users
  id uuid not null primary key,
  name text,
  email text,
  image text,
  constraint "users_id_fkey" foreign key ("id")
        references  next_auth.users (id) match simple
        on update no action
        on delete cascade -- if a user is deleted in NextAuth they will also be deleted in our public table.
);
alter table users enable row level security;
create policy "Can view own user data." on users for select using (next_auth.uid() = id);
create policy "Can update own user data." on users for update using (next_auth.uid() = id);
 
-- This trigger automatically creates a user entry when a new user signs up via NextAuth.
create function public.handle_new_user()
returns trigger as $$
begin
  insert into public.users (id, name, email, image)
  values (new.id, new.name, new.email, new.image);
  return new;
end;
$$ language plpgsql security definer;
create trigger on_auth_user_created
  after insert on next_auth.users
  for each row execute procedure public.handle_new_user();

现在,supabaseAccessToken 可在 session 对象上使用,并且可以传递给 supabase-js 客户端。这在任何环境中都有效:客户端、服务器端(API 路由、SSR),以及中间件边缘函数!

// Use `useSession()` or `unstable_getServerSession()` to get the NextAuth session.
 
const { supabaseAccessToken } = session
 
const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
  {
    global: {
      headers: {
        Authorization: `Bearer ${supabaseAccessToken}`,
      },
    },
  }
)
// Now you can query with RLS enabled.
const { data, error } = await supabase.from("users").select("*")

TypeScript

您可以将使用 Supabase CLI 生成的类型传递给 Supabase 客户端,以获得增强的类型安全性自动完成。

创建新的 supabase 客户端对象

import { createClient } from "@supabase/supabase-js"
import { Database } from "../database.types"
 
const supabase = createClient<Database>()

使用 supabaseAccessToken 扩展会话类型

为了使用 supabaseAccessToken 扩展 session 对象,我们需要在 types/next-auth.d.ts 文件中扩展 session 接口

types/next-auth.d.ts
import NextAuth, { type DefaultSession } from "next-auth"
 
declare module "next-auth" {
  // Returned by `useSession`, `getSession` and received as a prop on the `SessionProvider` React Context
  interface Session {
    // A JWT which can be used as Authorization header with supabase-js for RLS.
    supabaseAccessToken?: string
    user: {
      // The user's postal address
      address: string
    } & DefaultSession["user"]
  }
}
Auth.js © 巴拉兹·奥尔班 和团队 -2024