인증 API
구글 로그인, 토큰 재발급, 로그아웃, 사용자 정보 조회
개요
Soonlife 백엔드의 인증은 구글 ID 토큰 검증 + JWT 방식을 사용합니다. Expo 앱이 구글 로그인으로 받은 ID 토큰을 백엔드에 전달하면, 백엔드가 구글 서버에 검증한 뒤 자체 JWT(access/refresh)를 발급합니다.
인증이 필요한 엔드포인트는 Authorization: Bearer <access token> 헤더를 포함해야 합니다. 세션/쿠키는 사용하지 않습니다.
엔드포인트 목록
| 메서드 | 경로 | 인증 필요 | 설명 |
|---|---|---|---|
POST | /api/auth/google/ | 아니오 | 구글 ID 토큰 검증 + 로그인/회원가입 + JWT 발급 |
POST | /api/auth/token/refresh/ | 아니오 (refresh 토큰) | access 토큰 재발급 |
POST | /api/auth/logout/ | 예 | refresh 토큰 블랙리스트 처리 |
GET | /api/auth/me/ | 예 | 현재 로그인한 사용자 정보 조회 |
POST /api/auth/google/
구글 ID 토큰을 검증하고, 최초 로그인이면 회원가입까지 자동 처리한 뒤 JWT를 발급합니다.
요청
Body (application/json)
{
"id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6Ij..."
}| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
id_token | string | O | Expo 앱이 구글 로그인 후 받은 ID 토큰 |
응답
200 OK (기존 회원 로그인) / 201 Created (신규 회원가입)
{
"user": {
"id": 1,
"email": "user@gmail.com",
"name": "홍길동",
"date_joined": "2026-07-20T12:00:00+09:00"
},
"tokens": {
"access": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
},
"is_new_user": false
}| 응답 필드 | 타입 | 설명 |
|---|---|---|
user.id | number | 사용자 PK |
user.email | string | 이메일 |
user.name | string | 이름 |
user.date_joined | string | 가입일 (ISO 8601) |
tokens.access | string | access 토큰 (30분) |
tokens.refresh | string | refresh 토큰 (14일) |
is_new_user | boolean | 이번 요청으로 신규 가입되었는지 여부 |
에러 응답
| 상태 코드 | 상황 | 예시 |
|---|---|---|
400 Bad Request | id_token 누락 | {"id_token": ["이 필드는 필수 항목입니다."]} |
401 Unauthorized | 토큰 위조/만료/서명 불일치 | {"detail": "유효하지 않은 구글 ID 토큰입니다: ..."} |
401 Unauthorized | 클라이언트 ID(aud)가 허용 목록에 없음 | {"detail": "토큰의 클라이언트 ID(aud)가 허용된 값이 아닙니다."} |
401 Unauthorized | 이메일 미인증 구글 계정 | {"detail": "이메일이 인증되지 않은 구글 계정입니다."} |
aud 검증에 실패하면 서버의 GOOGLE_OAUTH_CLIENT_IDS 환경변수에 해당 클라이언트 ID가 등록되어 있는지 먼저 확인하세요.
POST /api/auth/token/refresh/
access 토큰 만료 시 refresh 토큰으로 재발급받습니다. 재발급된 refresh는 기존 것을 대체하며(Rotation), 기존 refresh는 즉시 폐기됩니다.
요청
{
"refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}| 필드 | 타입 | 필수 |
|---|---|---|
refresh | string | O |
응답
200 OK
{
"access": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}에러 응답
| 상태 코드 | 상황 |
|---|---|
401 Unauthorized | refresh 토큰이 유효하지 않거나 만료/블랙리스트됨 ({"detail": "Token is invalid", "code": "token_not_valid"}) |
POST /api/auth/logout/
전달받은 refresh 토큰을 블랙리스트에 등록해 더 이상 사용할 수 없게 만듭니다.
access 토큰은 만료 전까지는 계속 유효하므로, 클라이언트(Expo 앱)에서도 로컬에 저장된 access/refresh 토큰을 함께 폐기해야 합니다.
요청
쿠키 대신 Authorization: Bearer <access token> 헤더 필요.
{
"refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}| 필드 | 타입 | 필수 |
|---|---|---|
refresh | string | O |
응답
204 No Content — 본문 없음.
에러 응답
| 상태 코드 | 상황 |
|---|---|
400 Bad Request | refresh 필드 누락 또는 유효하지 않은 토큰 |
401 Unauthorized | access 토큰 미포함/만료 |
GET /api/auth/me/
현재 로그인한 사용자 정보를 조회합니다.
요청
Authorization: Bearer <access token> 헤더 필요.
응답
200 OK
{
"id": 1,
"email": "user@gmail.com",
"name": "홍길동",
"date_joined": "2026-07-20T12:00:00+09:00"
}에러 응답
| 상태 코드 | 상황 |
|---|---|
401 Unauthorized | access 토큰 미포함/만료/유효하지 않음 |
토큰 수명
| 토큰 | 유효기간 | 비고 |
|---|---|---|
| access | 30분 | Authorization: Bearer 헤더로 사용 |
| refresh | 14일 | 재발급 시 rotate, 이전 refresh는 즉시 폐기 |
구글 ID 토큰 검증 상세
별도의 구글 API 호출 없이, 전달받은 ID 토큰(JWT) 자체의 서명된 클레임을 디코딩해서 사용합니다 (google.oauth2.id_token.verify_oauth2_token).
| 클레임 | 용도 |
|---|---|
sub | 구글 계정 고유 식별자 → 사용자 조회/생성 키 (이메일이 아닌 이 값으로 계정을 식별) |
email | 이메일 저장 |
email_verified | 이메일 인증 여부 검증, 미인증 시 로그인 거부 |
name | 이름 저장 |
aud | 토큰 발급 대상 클라이언트 ID 검증 (GOOGLE_OAUTH_CLIENT_IDS 허용 목록과 대조) |
iss | 발급자가 구글(accounts.google.com)인지 검증 |
비밀번호, 프로필 사진 등 다른 정보는 저장하지 않으며, Google People API 등 별도 API도 호출하지 않습니다.