Supabase Adapter
리소스
설정
설치
npm install @supabase/supabase-js @auth/supabase-adapter
환경 변수
SUPABASE_URL
SUPABASE_SERVICE_ROLE_KEY
설정
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 Auth와 Next.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 설정에서 찾을 수 있습니다.
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();
이제 supabaseAccessToken
이 session
객체에서 사용 가능하며, 이를 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
인터페이스를 확장해야 합니다:
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"]
}
}