Skip to content
Migrating from NextAuth.js v4? Read our migration guide.

Supabase Adapter

리소스

설정

설치

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 Auth와 인터페이스하지 않는 독립적인 Auth 서버로, 다른 기능 세트를 제공합니다.

내장 이메일 서버, 전화 인증, 다중 인증(MFA / 2FA)과 같은 추가 기능을 제공하는 공식적으로 유지보수되는 Auth 서버를 찾고 있다면, Supabase AuthNext.js용 Auth Helpers를 사용하세요.

스키마

데이터베이스를 설정하려면 스키마에 설명된 대로 아래 SQL 스키마를 Supabase SQL Editor에 복사하세요.

또는 SQL Editor 페이지에서 NextAuth Quickstart 카드를 선택하거나, 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;
 
--
-- users 테이블 생성
--
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;
 
--- RLS 정책에서 사용할 uid() 함수
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
$$;
 
--
-- sessions 테이블 생성
--
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;
 
--
-- accounts 테이블 생성
--
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;
 
--
-- verification_tokens 테이블 생성
--
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;

NextAuth 스키마를 Supabase에 노출하기

API 설정에서 “Exposed schemas” 목록에 next_auth를 추가하여 Serverless API를 통해 next_auth 스키마를 노출할 수 있습니다.

로컬에서 개발할 때는 Supabase CLI로 생성된 supabase 폴더 안의 config.toml 파일에서 schemas 배열에 next_auth를 추가하세요.

Advanced usage

행 단위 보안(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"
 
// 각 옵션에 대한 자세한 정보(및 전체 옵션 목록)는 다음 링크를 참조하세요
// https://authjs.dev/reference/core/types#authconfig
export const { handlers, auth, signIn, signOut } = NextAuth({
  // https://authjs.dev/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를 클라이언트에 주입하기

예를 들어, 다음과 같은 공개 스키마가 있다고 가정해 보겠습니다:

-- 참고: 이 테이블은 사용자 데이터를 포함합니다. 사용자는 자신의 데이터만 볼 수 있고 업데이트할 수 있어야 합니다.
create table users (
  -- next_auth.users의 UUID
  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 -- NextAuth에서 사용자가 삭제되면 공개 테이블에서도 삭제됩니다.
);
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);
 
-- 이 트리거는 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();

이제 supabaseAccessTokensession 객체에서 사용 가능하며, 이를 supabase-js 클라이언트에 전달할 수 있습니다. 이 방법은 클라이언트 측, 서버 측(API 라우트, SSR), 그리고 미들웨어 엣지 함수에서도 동작합니다!

// `useSession()` 또는 `unstable_getServerSession()`을 사용하여 NextAuth 세션을 가져옵니다.
 
const { supabaseAccessToken } = session
 
const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
  {
    global: {
      headers: {
        Authorization: `Bearer ${supabaseAccessToken}`,
      },
    },
  }
)
// 이제 RLS가 활성화된 상태로 쿼리를 실행할 수 있습니다.
const { data, error } = await supabase.from("users").select("*")

TypeScript

Supabase CLI로 생성된 타입을 Supabase Client에 전달하면 향상된 타입 안전성과 자동 완성 기능을 얻을 수 있습니다.

새로운 supabase 클라이언트 객체를 생성하는 방법:

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

supabaseAccessToken로 세션 타입 확장하기

session 객체에 supabaseAccessToken을 추가하려면 types/next-auth.d.ts 파일에서 session 인터페이스를 확장해야 합니다:

types/next-auth.d.ts
import NextAuth, { type DefaultSession } from "next-auth"
 
declare module "next-auth" {
  // `useSession`, `getSession`에서 반환되며 `SessionProvider` React Context의 prop으로 전달됨
  interface Session {
    // RLS와 함께 supabase-js에서 Authorization 헤더로 사용할 수 있는 JWT
    supabaseAccessToken?: string
    user: {
      // 사용자의 우편 주소
      address: string
    } & DefaultSession["user"]
  }
}
Auth.js © Balázs Orbán and Team - 2025