Supabase Auth - Session, Token, RLS의 관계
| 구분 | Session 방식 | Token 방식 |
|---|---|---|
| 클라이언트가 보통 가지고 있는 것 | Session ID | Token |
| 사용자 정보 | 서버 Session 저장소에 있음 | JWT라면 Token 내부에 있을 수 있음 |
| 인증 확인 | Session ID로 Session 조회 | Token의 서명/내용 검증 |
| DB 조회 | Session 저장소 조회가 필요 | JWT 인증 자체에는 반드시 필요하지 않음 |
| 사용자 식별 | Session에 연결된 user ID | Token의 sub 등 |
| 권한 확인 | Session/DB 정보 활용 | Token 정보 또는 DB 정보 활용 |
◆ Supabase Auth란?
Supabase에서는 Supabase Auth를 이용하여 회원가입, 로그인, 로그아웃 등의 인증 기능을 구현할 수 있다.
로그인 과정에서 가장 중요한 개념은 다음과 같다.
auth.usersSessionaccess_tokenrefresh_tokenauth.uid()- RLS(Row Level Security)
public.users
이들은 서로 연결되어 있지만 각각 담당하는 역할은 다르다.
◆ 1. Supabase Auth의 사용자
Supabase에서 사용자가 이메일과 비밀번호로 로그인하면 먼저 Supabase Auth가 사용자의 인증 정보를 확인한다.
Supabase Auth에서는 사용자를 auth.users에 관리한다.
예를 들어:
auth.users
┌──────────────────────────────────────┐
│ id │
│ dc381699-8ade-4752-8905-7e752a1a2cbc │
│ │
│ email │
│ admin@test.com │
└──────────────────────────────────────┘
여기서 id는 해당 사용자를 식별하는 UUID이다.
이 UUID는 이후 Session, JWT, RLS 등에서 사용된다.
◆ 2. 로그인하면 Session이 발급된다
React에서 Supabase 로그인을 수행할 때는 다음과 같이 사용할 수 있다.
const { data, error } =
await supabase.auth.signInWithPassword({
email,
password,
})
로그인이 성공하면 Supabase는 Session을 반환한다.
Session은 단순히 하나의 토큰이 아니라 현재 로그인 상태를 유지하기 위한 인증 정보의 묶음이다.
Session
├── access_token
├── refresh_token
├── expires_at
├── expires_in
├── token_type
└── user
└── id
◆ 3. Session과 UUID의 관계
Session 자체가 auth.users의 UUID인 것은 아니다.
Session 안에 있는:
session.user.id
가 auth.users.id와 동일한 UUID를 가진다.
예를 들어:
auth.users.id
│
│ 동일한 UUID
▼
session.user.id
따라서 로그인한 사용자가:
auth.users.id
= dc381699-8ade-4752-8905-7e752a1a2cbc
라면 Session의 user.id도 동일하다.
◆ 4. Access Token
Session에서 가장 중요한 값 중 하나가 access_token이다.
Session
└── access_token
access_token은 실제로 Supabase에 요청을 보낼 때 현재 사용자가 인증된 사용자임을 증명하는 데 사용되는 토큰이다.
Supabase의 Access Token은 JWT(JSON Web Token) 형식이다.
JWT 내부에는 사용자 식별에 필요한 정보가 포함되어 있으며, 대표적으로 sub 값이 있다.
JWT Payload
┌──────────────────────────────────────┐
│ sub: "dc381699-8ade-4752-8905-..." │
│ aud: "authenticated" │
│ role: "authenticated" │
│ exp: ... │
└──────────────────────────────────────┘
여기서 sub는 해당 Auth 사용자의 UUID를 나타낸다.
즉:
access_token
↓
JWT
↓
sub
↓
auth.users.id
로 연결된다.
◆ 5. Access Token의 만료 시간
Session에는 다음과 같은 값이 있다.
expires_in: 3600
3600초이므로:
3600초
= 60분
= 1시간
이다.
즉 현재 Access Token은 1시간 동안 유효하다.
JWT 내부에도:
iat = 발급 시간
exp = 만료 시간
이 들어있으며,
exp - iat = 3600초
가 된다.
◆ 6. Access Token이 만료되면 어떻게 될까?
Access Token이 1시간 후 만료된다고 해서 사용자가 바로 로그아웃되는 것은 아니다.
Session에는 refresh_token도 존재한다.
Session
├── access_token
└── refresh_token
Access Token이 만료되거나 만료되기 전에 Supabase가 Refresh Token을 사용하여 새로운 Access Token을 발급할 수 있다.
Access Token
│
│ 1시간 후 만료
▼
Refresh Token 사용
│
▼
새로운 Access Token 발급
따라서 사용자는 별도로 로그아웃하지 않는 한 계속 로그인 상태를 유지할 수 있다.
◆ 7. Token이 갱신되면 User도 새로 만들어질까?
그렇지 않다.
Access Token이 갱신되는 것은 같은 사용자의 새로운 인증 토큰이 발급되는 것이다.
예를 들어 처음 Session이:
Session
├── access_token = Token A
└── user.id = AAA
였다면 Access Token 갱신 후에는:
Session
├── access_token = Token B
└── user.id = AAA
가 된다.
즉:
Token A
↓ 만료
Token B
↓
같은 사용자 AAA
이다.
사용자의 UUID가 새로운 값으로 변경되는 것이 아니다.
◆ 8. Session과 User는 다르다
애플리케이션에서는 Session과 사용자 프로필 데이터를 구분하는 것이 중요하다.
Session
Supabase Auth가 관리하는 인증 상태이다.
Session
├── access_token
├── refresh_token
├── expires_at
└── user
User
Supabase Auth에서 관리하는 사용자 정보이다.
auth.users
├── id
├── email
├── role
├── user_metadata
└── ...
그리고 실제 애플리케이션에서 필요한 추가 정보는 별도의 public.users 테이블에 저장할 수 있다.
public.users
├── id
├── auth_id
├── name
├── role
└── ...
◆ 9. auth.users와 public.users 연결
일반적으로 애플리케이션에서 사용하는 사용자 프로필 정보를 public.users에 저장하고, Supabase Auth 사용자의 UUID와 연결할 수 있다.
예를 들어:
auth.users
┌──────────────────────────────────────┐
│ id │
│ dc381699-8ade-4752-8905-7e752a1a2cbc │
│ email = admin@test.com │
└──────────────────────────────────────┘
│
│ auth_id
▼
public.users
┌──────────────────────────────────────┐
│ id │
│ auth_id = dc381699-... │
│ name = 관리자 │
│ role = admin │
└──────────────────────────────────────┘
로그인 성공 후:
const profile = await fetchUserProfile(data.user.id)
와 같이 Auth 사용자의 UUID를 이용하여 public.users의 프로필 정보를 조회할 수 있다.
예를 들어:
.eq('auth_id', authId)
와 같이 조회한다.
여기서 중요한 점은 public.users가 로그인 여부를 판단하는 테이블은 아니라는 것이다.
로그인 인증은 Supabase Auth가 담당한다.
◆ 10. RLS(Row Level Security)
RLS는 사용자가 로그인할 수 있는지를 결정하는 기능이 아니다.
역할을 정확하게 구분하면 다음과 같다.
Supabase Auth
↓
"이메일과 비밀번호가 올바른가?"
↓
인증(Authentication)
로그인에 성공한 후:
RLS
↓
"인증된 사용자가 어떤 데이터를 접근할 수 있는가?"
↓
인가(Authorization)
즉:
Auth는 누구인지 확인하고, RLS는 무엇을 할 수 있는지 결정한다.
◆ 11. auth.uid()란?
RLS 정책에서는 다음과 같은 함수를 사용할 수 있다.
auth.uid()
auth.uid()는 현재 요청을 보낸 인증된 사용자의 UUID를 반환한다.
예를 들어 로그인한 사용자의 UUID가:
dc381699-8ade-4752-8905-7e752a1a2cbc
라면:
auth.uid()
도 해당 UUID를 기준으로 동작한다.
이를 이용하여 자신의 데이터만 조회하도록 RLS 정책을 만들 수 있다.
예:
auth.uid() = auth_id
의 의미는:
현재 인증된 사용자의 UUID와 해당 행의
auth_id가 같은 경우에만 접근을 허용한다.
◆ 12. 전체 인증 흐름
지금까지의 내용을 하나의 흐름으로 정리하면 다음과 같다.
사용자가 이메일/비밀번호 입력
↓
signInWithPassword()
↓
Supabase Auth
↓
auth.users 확인
↓
인증 성공
↓
Session 발급
↓
┌────────┴────────┐
↓ ↓
access_token refresh_token
↓
JWT
↓
사용자 UUID 확인
↓
Supabase DB 요청
↓
auth.uid()
↓
RLS
↓
접근 권한 확인
↓
허용된 데이터 반환
그리고 애플리케이션 프로필 데이터가 필요하다면:
auth.users.id
│
│ 동일한 UUID
▼
public.users.auth_id
│
▼
프로필 정보 조회
가 추가된다.
◆ 13. Session은 브라우저에 저장되어도 괜찮을까?
React SPA에서 Supabase JS Client를 사용하는 경우 Session 정보가 브라우저의 저장소에 보이는 것은 정상적인 동작이다.
개발자 도구의 Local Storage에서 다음과 같은 정보를 볼 수 있다.
Session
├── access_token
├── refresh_token
├── expires_at
├── expires_in
└── user
하지만 여기서 중요한 보안 문제가 있다.
access_token이나 refresh_token이 공격자에게 탈취되면 인증된 사용자처럼 요청을 수행할 수 있기 때문이다.
따라서:
- XSS 방지
- 안전한 프론트엔드 코드 작성
- 적절한 Session 설정
- 반드시 RLS 적용
- 클라이언트에서 전달한 사용자 ID만 믿지 않기
등이 중요하다.
특히 DB 접근 권한은 클라이언트 코드가 아니라 RLS를 통해 데이터베이스에서 최종적으로 검증해야 한다.
◆ 14. 핵심 정리
Supabase 인증을 이해할 때 다음 관계를 기억하면 된다.
┌─────────────────────────────┐
│ Supabase Auth │
│ │
│ auth.users │
│ │ │
│ │ id │
│ ▼ │
│ Session │
│ ┌───────────┐ │
│ │access_token│ │
│ │refresh_token│ │
│ │user.id │ │
│ └───────────┘ │
└─────────────┬───────────────┘
│
▼
auth.uid()
│
▼
RLS
│
▼
DB 접근 권한 결정
│
▼
public.users 등
애플리케이션 데이터
핵심적으로 기억할 것은 다음과 같다.
auth.users.id는 Supabase Auth 사용자의 고유 UUID이다.- Session 자체가 UUID는 아니다.
- Session 안의
user.id가auth.users.id와 연결된다. access_token은 실제 인증에 사용되는 JWT이다.- 현재 Access Token은
expires_in: 3600이므로 1시간 동안 유효하다. - Access Token이 만료되면 Refresh Token을 이용해 새로운 Access Token을 받을 수 있다.
- Token이 갱신되어도 같은 사용자의 UUID는 유지된다.
- Supabase Auth는 인증(Authentication)을 담당한다.
- RLS는 인증된 사용자의 DB 접근 권한(Authorization)을 담당한다.
auth.uid()를 이용하면 현재 인증된 사용자의 UUID를 기준으로 RLS 정책을 만들 수 있다.public.users는 애플리케이션의 사용자 프로필 정보를 저장하는 용도로 사용할 수 있다.- 브라우저에서 Session 정보가 보일 수 있으므로 XSS와 RLS 등 보안 대책이 중요하다.
댓글남기기