포스트

[Project A] 인증 도메인 기술 기획서

📋 개요

이 문서는 Spring Boot 기반 WAS의 인증 도메인에 대한 기술 기획서입니다.

JWT 토큰과 PKCE(Proof Key for Code Exchange) 방식을 활용한 보안성 높은 인증 시스템을 제공합니다.


🎯 주요 기능

1. OAuth2 인증 지원

  • Google OAuth2
  • Kakao OAuth2
  • PKCE 방식으로 보안성 강화

2. JWT 토큰 기반 인증

  • Access Token: 짧은 만료 시간 (API 요청 인증)
  • Refresh Token: 긴 만료 시간 (토큰 갱신)
  • 쿠키 기반 토큰 저장

3. 보안 기능

  • CSRF 공격 방지 (State 매개변수)
  • 토큰 자동 갱신
  • 안전한 로그아웃

🏗️ 시스템 아키텍처

계층 구조

1
2
3
4
5
6
7
8
9
┌─────────────────────┐
│   Presentation      │  ← API Controller, Request/Response DTO
├─────────────────────┤
│   Application       │  ← Service, Repository Interface
├─────────────────────┤
│   Domain            │  ← Business Logic, Domain Model
├─────────────────────┤
│   Infrastructure    │  ← External Client, JPA Entity, Redis
└─────────────────────┘

주요 컴포넌트

계층컴포넌트역할
PresentationAuthenticationControllerREST API 엔드포인트 제공
ApplicationAuthenticationService인증 비즈니스 로직 처리
ApplicationOAuth2UrlServiceOAuth2 URL 생성
DomainJwtProvider/JwtResolverJWT 토큰 생성/검증
DomainOAuth2TokenServiceOAuth2 토큰 획득
InfrastructureGoogleTokenClientGoogle OAuth2 API 호출
InfrastructureKakaoTokenClientKakao OAuth2 API 호출

🔄 인증 플로우

1. OAuth2 로그인 프로세스

sequenceDiagram
    participant Client
    participant API
    participant OAuth2Provider
    participant Redis
    participant DB

    Client->>API: 1. OAuth2 URL 요청 (code_verifier, redirect_path)
    API->>Redis: 2. OAuth2Context 저장 (state, code_verifier, redirect_path)
    API->>Client: 3. OAuth2 URL 반환 (state 포함)
    
    Client->>OAuth2Provider: 4. 사용자 인증 및 권한 동의
    OAuth2Provider->>Client: 5. Authorization Code 반환 (code, state)
    
    Client->>API: 6. 로그인 요청 (code, state)
    API->>Redis: 7. OAuth2Context 조회 및 검증
    API->>OAuth2Provider: 8. Access Token 요청 (code, code_verifier)
    OAuth2Provider->>API: 9. Access Token 반환
    API->>OAuth2Provider: 10. 사용자 프로필 요청
    OAuth2Provider->>API: 11. 사용자 프로필 반환
    
    API->>DB: 12. 회원 정보 조회/생성
    API->>API: 13. JWT 토큰 생성 (Access + Refresh)
    API->>Redis: 14. Refresh Token 저장
    API->>Client: 15. 로그인 성공 (쿠키로 토큰 전달)

2. 토큰 갱신 프로세스

sequenceDiagram
    participant Client
    participant API
    participant Redis
    participant DB

    Client->>API: 1. 토큰 갱신 요청 (Refresh Token)
    API->>API: 2. Refresh Token 검증
    API->>Redis: 3. 저장된 Refresh Token 조회
    API->>API: 4. 토큰 일치 검증
    API->>DB: 5. 회원 정보 조회
    API->>API: 6. 새로운 Access Token 생성
    API->>Redis: 7. Refresh Token 사용 횟수 증가
    API->>Client: 8. 새로운 Access Token 반환

📊 데이터 모델

JWT Claims 구조

Access Token Claims

1
2
3
4
5
6
7
{
  "id": "회원 ID (Long)",
  "nickname": "회원 닉네임",
  "profileImage": "프로필 이미지 URL",
  "email": "이메일 주소",
  "authorities": ["ROLE_NORMAL"]
}

Refresh Token Claims

1
2
3
{
  "id": "회원 ID (Long)"
}

Redis 저장 데이터

OAuth2Context (임시 저장)

1
2
3
4
5
6
7
8
{
  "state": "CSRF 방지용 랜덤 문자열",
  "provider": "GOOGLE | KAKAO",
  "codeVerifier": "PKCE code verifier",
  "redirectPath": "클라이언트 리다이렉트 경로",
  "expiresAt": "만료 시간 (5분)",
  "ttl": "Redis TTL (초)"
}

RefreshToken

1
2
3
4
5
6
7
{
  "memberId": "회원 ID",
  "token": "Refresh Token 문자열",
  "useCount": "사용 횟수",
  "expiresAt": "만료 시간",
  "ttl": "Redis TTL (초)"
}

🔐 보안 정책

1. PKCE (Proof Key for Code Exchange)

  • Code Verifier: 클라이언트가 생성하는 43-128자의 랜덤 문자열
  • Code Challenge: Code Verifier의 SHA256 해시값 (Base64 URL 인코딩)
  • Authorization Code Interception Attack 방지

2. State 매개변수

  • CSRF 공격 방지
  • 32바이트 랜덤 값을 Base64 URL 인코딩
  • 5분 만료 시간 설정

3. JWT 토큰 정책

  • Access Token: 짧은 만료 시간 (일반적으로 15분-1시간)
  • Refresh Token: 긴 만료 시간 (일반적으로 7-30일)
  • HttpOnly, Secure, SameSite=None 쿠키 설정

4. 토큰 저장 정책

  • Access Token: 메모리 내 저장 (쿠키)
  • Refresh Token: Redis에 저장 (회원 ID 기준)
  • 로그아웃 시 모든 토큰 무효화

🌐 API 명세

1. OAuth2 URL 생성

1
2
3
4
5
6
7
8
POST /auth/oauth2/url
Content-Type: application/json

{
  "provider": "GOOGLE",
  "codeVerifier": "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk",
  "redirectPath": "/home"
}

응답:

1
2
3
4
5
6
{
  "url": "https://accounts.google.com/oauth/authorize?client_id=...",
  "state": "BjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk",
  "provider": "GOOGLE",
  "expiresAt": "2024-01-15T10:05:00"
}

2. OAuth2 로그인

1
2
3
4
5
6
7
8
POST /auth/oauth2/login
Content-Type: application/json

{
  "provider": "GOOGLE",
  "code": "4/0AX4XfWjJKZ...",
  "state": "BjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
}

응답:

1
2
3
4
5
6
7
8
9
{
  "memberInfo": {
    "id": 1,
    "nickname": "홍길동#1234",
    "profileImage": "member/profile/image/01234567.jpg",
    "email": "user@example.com"
  },
  "redirectPath": "/home"
}

3. 로그아웃

1
2
POST /auth/logout
Cookie: access-token={access_token}

4. 토큰 갱신

1
2
POST /auth/token/refresh
Cookie: refresh-token={refresh_token}

⚙️ 설정 및 환경변수

OAuth2 설정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
app:
  oauth2:
    google:
      client-id: ${GOOGLE_CLIENT_ID}
      client-secret: ${GOOGLE_CLIENT_SECRET}
      authorization-url: https://accounts.google.com/o/oauth2/v2/auth
      token-url: https://oauth2.googleapis.com/token
      user-info-url: https://www.googleapis.com/oauth2/v2/userinfo
      redirect-uri: http://localhost:3000/auth/callback/google
      scope: email
    kakao:
      client-id: ${KAKAO_CLIENT_ID}
      client-secret: ${KAKAO_CLIENT_SECRET}
      authorization-url: https://kauth.kakao.com/oauth/authorize
      token-url: https://kauth.kakao.com/oauth/token
      user-info-url: https://kapi.kakao.com/v2/user/me
      redirect-uri: http://localhost:3000/auth/callback/kakao
      scope: account_email

JWT 설정

1
2
3
4
5
6
7
8
9
10
app:
  jwt:
    access-token:
      token-key: access-token
      secret-key: ${JWT_ACCESS_SECRET}
      expires-in: 1800 # 30분
    refresh-token:
      token-key: refresh-token
      secret-key: ${JWT_REFRESH_SECRET}
      expires-in: 2592000 # 30일

🚨 예외 처리

OAuth2 관련 예외

예외 코드설명HTTP 상태
OAUTH-001지원하지 않는 OAuth2 제공자400
OAUTH-002Code Challenge 생성 실패500
OAUTH-003OAuth2 Context를 찾을 수 없음404
OAUTH-004OAuth2 제공자에서 토큰 획득 실패500
OAUTH-005OAuth2 제공자에서 사용자 프로필 획득 실패500

인증/인가 관련 예외

예외 코드설명HTTP 상태
ACT-001Access Token을 찾을 수 없음401
ACT-002유효하지 않은 Access Token401
ACT-003만료된 Access Token401
RFT-001Refresh Token을 찾을 수 없음404
RFT-002유효하지 않은 Refresh Token400
RFT-003만료된 Refresh Token400
RFT-004Refresh Token 불일치400

🔧 기술 스택

Backend Framework

  • Spring Boot 3.2.2
  • Spring Security 6
  • Spring Data JPA
  • Spring Data Redis

외부 연동

  • OpenFeign: OAuth2 Provider API 호출
  • JWT: jjwt 라이브러리 사용

데이터 저장소

  • Redis: 세션 및 임시 데이터 저장
  • MySQL: 회원 정보 저장

📈 확장 계획

1. 추가 OAuth2 제공자 지원

  • GitHub OAuth2
  • Naver OAuth2
  • Facebook OAuth2

2. 보안 강화

  • JWT 토큰 블랙리스트 관리
  • 비정상적인 로그인 시도 감지
  • 디바이스 기반 인증

3. 모니터링

  • 로그인 성공/실패 통계
  • 토큰 사용 패턴 분석
  • 보안 이벤트 로깅

📚 참고 자료

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.