跳至內容
從 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 結構描述中。它是一個獨立的驗證伺服器,不與 Supabase 驗證介面互動,因此提供不同的功能集。

如果您正在尋找具有額外功能(例如內建電子郵件伺服器電話驗證,和 多重要素驗證 (MFA / 2FA))的官方維護驗證伺服器,請搭配 Supabase 驗證使用 適用於 Next.js 的驗證輔助程式

結構描述

依照我們的主要 結構描述中所述設定您的資料庫,方法是在 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 CLI產生的 supabase 資料夾中的 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.dev.org.tw/reference/core/types#authconfig
export const { handlers, auth, signIn, signOut } = NextAuth({
  // https://authjs.dev.org.tw/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 擴展 session 類型

為了使用 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 © Balázs Orbán 及團隊 -2024