인증·키

로그인 세션을 쿠키에 두고, 서버에만 둘 키를 가립니다.

이메일로 로그인

대시보드 Authentication에서 이메일 제공자를 켠 뒤, 서버 함수에서 비밀번호 로그인을 호출한다. 성공하면 세션이 쿠키에 남는다. 그 쿠키를 다음 요청의 서버 클라이언트가 읽는다.

'use server'

import { createClient } from '@/lib/supabase/server'

export async function signIn(formData) {
  const supabase = await createClient()
  const { error } = await supabase.auth.signInWithPassword({
    email: formData.get('email'),
    password: formData.get('password')
  })
  return { error: error ? error.message : null }
}
import { signIn } from './actions'

export default function Page() {
  return (
    <form action={signIn}>
      <label htmlFor="email">이메일</label>
      <input id="email" name="email" type="email" autoComplete="username" />
      <label htmlFor="password">비밀번호</label>
      <input id="password" name="password" type="password" autoComplete="current-password" />
      <button type="submit">로그인</button>
    </form>
  )
}

누가 로그인했는지

서버에서는 getClaims()로 토큰 서명을 확인한 뒤 신원을 쓴다. getSession()은 쿠키 안의 값을 검증 없이 읽으므로 서버에서 권한 판단에 쓰지 않는다.

const supabase = await createClient()
const { data } = await supabase.auth.getClaims()
const user = data?.claims ?? null

세션이 오래되면 proxy.js(Next.js 15 이하는 middleware.js)가 getClaims()를 호출해 쿠키를 갱신한다. 이 파일을 빼거나, 갱신된 응답 대신 다른 응답을 돌려주면 사용자가 갑자기 로그아웃된다. 전체 코드는 공식 문서를 복사한다.

키를 나누는 기준

  • publishable 키만 NEXT_PUBLIC_에 둔다. 이 키는 RLS를 통과해야 행을 본다.
  • secret 키는 서버의 비공개 환경 변수에만 둔다. 브라우저 코드, 채팅, GitHub에 붙여 넣지 않는다.
  • 이미 공개됐으면 대시보드에서 그 키를 폐기하고 새로 만든다. 파일에서 지우는 것만으로는 이력에 남는다.

문서: Creating a Supabase client for SSR (새 창)