phonebill/design/backend/api/auth-service-api.yaml
2025-09-09 01:12:14 +09:00

820 lines
25 KiB
YAML

openapi: 3.0.3
info:
title: Auth Service API
description: |
통신요금 관리 서비스의 사용자 인증 및 인가를 담당하는 Auth Service API
## 주요 기능
- 사용자 로그인/로그아웃 처리
- JWT 토큰 기반 인증
- Redis를 통한 세션 관리
- 서비스별 접근 권한 검증
- 토큰 갱신 처리
## 보안 고려사항
- 5회 연속 로그인 실패 시 30분간 계정 잠금
- JWT Access Token: 30분 만료
- JWT Refresh Token: 24시간 만료
- Redis 세션 캐싱 (TTL: 30분, 자동로그인 시 24시간)
version: 1.0.0
contact:
name: Backend Development Team
email: backend@mvno.com
license:
name: Private
servers:
- url: http://localhost:8081
description: Development server
- url: https://api-dev.mvno.com
description: Development environment
- url: https://api.mvno.com
description: Production environment
tags:
- name: Authentication
description: 사용자 인증 관련 API
- name: Authorization
description: 사용자 권한 확인 관련 API
- name: Token Management
description: 토큰 관리 관련 API
- name: Session Management
description: 세션 관리 관련 API
paths:
/auth/login:
post:
tags:
- Authentication
summary: 사용자 로그인
description: |
MVNO 고객의 로그인을 처리합니다.
## 비즈니스 로직
- UFR-AUTH-010 유저스토리 구현
- 로그인 시도 횟수 확인 (최대 5회)
- 비밀번호 검증
- JWT 토큰 생성 (Access Token: 30분, Refresh Token: 24시간)
- Redis 세션 생성 및 캐싱
- 로그인 이력 기록
## 보안 정책
- 5회 연속 실패 시 30분간 계정 잠금
- 비밀번호 해싱 검증 (bcrypt)
- IP 기반 로그인 이력 추적
operationId: login
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/LoginRequest'
example:
userId: "mvno001"
password: "securePassword123!"
autoLogin: false
responses:
'200':
description: 로그인 성공
content:
application/json:
schema:
$ref: '#/components/schemas/LoginResponse'
example:
success: true
message: "로그인이 성공적으로 완료되었습니다."
data:
accessToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
refreshToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
expiresIn: 1800
user:
userId: "mvno001"
userName: "홍길동"
phoneNumber: "010-1234-5678"
permissions:
- "BILL_INQUIRY"
- "PRODUCT_CHANGE"
'401':
description: 인증 실패
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
examples:
invalid_credentials:
summary: 잘못된 인증 정보
value:
success: false
error:
code: "AUTH_001"
message: "ID 또는 비밀번호를 확인해주세요"
details: "입력된 인증 정보가 올바르지 않습니다."
account_locked:
summary: 계정 잠금 (5회 실패)
value:
success: false
error:
code: "AUTH_002"
message: "5회 연속 실패하여 30분간 계정이 잠금되었습니다."
details: "30분 후 다시 시도해주세요."
account_temp_locked:
summary: 계정 일시 잠금
value:
success: false
error:
code: "AUTH_003"
message: "계정이 잠금되었습니다. 30분 후 다시 시도해주세요."
details: "이전 5회 연속 실패로 인한 임시 잠금 상태입니다."
'400':
description: 잘못된 요청
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
success: false
error:
code: "VALIDATION_ERROR"
message: "요청 데이터가 올바르지 않습니다."
details: "userId는 필수 입력 항목입니다."
'500':
description: 서버 내부 오류
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
success: false
error:
code: "INTERNAL_SERVER_ERROR"
message: "서버 내부 오류가 발생했습니다."
details: "잠시 후 다시 시도해주세요."
/auth/logout:
post:
tags:
- Authentication
summary: 사용자 로그아웃
description: |
현재 사용자의 로그아웃을 처리합니다.
## 비즈니스 로직
- Redis 세션 삭제
- 로그아웃 이력 기록
- 클라이언트의 토큰 무효화 안내
operationId: logout
security:
- BearerAuth: []
responses:
'200':
description: 로그아웃 성공
content:
application/json:
schema:
$ref: '#/components/schemas/SuccessResponse'
example:
success: true
message: "로그아웃이 성공적으로 완료되었습니다."
'401':
description: 인증되지 않은 요청
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
success: false
error:
code: "UNAUTHORIZED"
message: "인증이 필요합니다."
details: "유효한 토큰이 필요합니다."
'500':
description: 서버 내부 오류
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
/auth/verify:
get:
tags:
- Token Management
summary: JWT 토큰 검증
description: |
JWT 토큰의 유효성을 검증하고 사용자 정보를 반환합니다.
## 비즈니스 로직
- JWT 토큰 유효성 검사
- Redis 세션 확인 (Cache-Aside 패턴)
- 세션 미스 시 DB에서 재조회 후 캐시 갱신
- 토큰 만료 검사
operationId: verifyToken
security:
- BearerAuth: []
responses:
'200':
description: 토큰 검증 성공
content:
application/json:
schema:
$ref: '#/components/schemas/TokenVerifyResponse'
example:
success: true
message: "토큰이 유효합니다."
data:
valid: true
user:
userId: "mvno001"
userName: "홍길동"
phoneNumber: "010-1234-5678"
permissions:
- "BILL_INQUIRY"
- "PRODUCT_CHANGE"
expiresIn: 1200
'401':
description: 토큰 무효 또는 만료
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
examples:
token_expired:
summary: 토큰 만료
value:
success: false
error:
code: "TOKEN_EXPIRED"
message: "토큰이 만료되었습니다."
details: "새로운 토큰을 발급받아주세요."
token_invalid:
summary: 유효하지 않은 토큰
value:
success: false
error:
code: "TOKEN_INVALID"
message: "유효하지 않은 토큰입니다."
details: "올바른 토큰을 제공해주세요."
'500':
description: 서버 내부 오류
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
/auth/refresh:
post:
tags:
- Token Management
summary: 토큰 갱신
description: |
Refresh Token을 사용하여 새로운 Access Token을 발급합니다.
## 비즈니스 로직
- Refresh Token 유효성 검증
- 새로운 Access Token 생성 (30분 만료)
- Redis 세션 갱신
- 토큰 갱신 이력 기록
operationId: refreshToken
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/RefreshTokenRequest'
example:
refreshToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
responses:
'200':
description: 토큰 갱신 성공
content:
application/json:
schema:
$ref: '#/components/schemas/RefreshTokenResponse'
example:
success: true
message: "토큰이 성공적으로 갱신되었습니다."
data:
accessToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
expiresIn: 1800
'401':
description: Refresh Token 무효 또는 만료
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
success: false
error:
code: "REFRESH_TOKEN_INVALID"
message: "Refresh Token이 유효하지 않습니다."
details: "다시 로그인해주세요."
'500':
description: 서버 내부 오류
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
/auth/permissions:
get:
tags:
- Authorization
summary: 사용자 권한 조회
description: |
현재 사용자의 서비스 접근 권한을 조회합니다.
## 비즈니스 로직
- UFR-AUTH-020 유저스토리 구현
- 사용자 권한 정보 조회
- 서비스별 접근 권한 확인
- Redis 캐시 우선 조회 (Cache-Aside 패턴)
operationId: getUserPermissions
security:
- BearerAuth: []
responses:
'200':
description: 권한 조회 성공
content:
application/json:
schema:
$ref: '#/components/schemas/PermissionsResponse'
example:
success: true
message: "권한 정보를 성공적으로 조회했습니다."
data:
userId: "mvno001"
permissions:
- permission: "BILL_INQUIRY"
description: "요금 조회 서비스"
granted: true
- permission: "PRODUCT_CHANGE"
description: "상품 변경 서비스"
granted: true
'401':
description: 인증되지 않은 요청
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'500':
description: 서버 내부 오류
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
/auth/permissions/check:
post:
tags:
- Authorization
summary: 특정 서비스 접근 권한 확인
description: |
사용자가 특정 서비스에 접근할 권한이 있는지 확인합니다.
## 비즈니스 로직
- 서비스별 접근 권한 검증
- BILL_INQUIRY: 요금 조회 서비스 권한
- PRODUCT_CHANGE: 상품 변경 서비스 권한
- Redis 세션 데이터 기반 권한 확인
operationId: checkPermission
security:
- BearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/PermissionCheckRequest'
example:
serviceType: "BILL_INQUIRY"
responses:
'200':
description: 권한 확인 완료
content:
application/json:
schema:
$ref: '#/components/schemas/PermissionCheckResponse'
examples:
permission_granted:
summary: 권한 있음
value:
success: true
message: "서비스 접근 권한이 확인되었습니다."
data:
serviceType: "BILL_INQUIRY"
hasPermission: true
permissionDetails:
permission: "BILL_INQUIRY"
description: "요금 조회 서비스"
granted: true
permission_denied:
summary: 권한 없음
value:
success: true
message: "서비스 접근 권한이 없습니다."
data:
serviceType: "BILL_INQUIRY"
hasPermission: false
permissionDetails:
permission: "BILL_INQUIRY"
description: "요금 조회 서비스"
granted: false
'400':
description: 잘못된 요청
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
success: false
error:
code: "VALIDATION_ERROR"
message: "serviceType은 필수 입력 항목입니다."
details: "BILL_INQUIRY 또는 PRODUCT_CHANGE 값을 입력해주세요."
'401':
description: 인증되지 않은 요청
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'500':
description: 서버 내부 오류
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
/auth/user-info:
get:
tags:
- Session Management
summary: 사용자 정보 조회
description: |
현재 인증된 사용자의 상세 정보를 조회합니다.
## 비즈니스 로직
- JWT 토큰에서 사용자 식별
- Redis 세션 우선 조회
- 캐시 미스 시 DB 조회 후 캐시 갱신
- 사용자 기본 정보 및 권한 정보 반환
operationId: getUserInfo
security:
- BearerAuth: []
responses:
'200':
description: 사용자 정보 조회 성공
content:
application/json:
schema:
$ref: '#/components/schemas/UserInfoResponse'
example:
success: true
message: "사용자 정보를 성공적으로 조회했습니다."
data:
userId: "mvno001"
userName: "홍길동"
phoneNumber: "010-1234-5678"
email: "hong@example.com"
status: "ACTIVE"
lastLoginAt: "2024-01-15T09:30:00Z"
permissions:
- "BILL_INQUIRY"
- "PRODUCT_CHANGE"
'401':
description: 인증되지 않은 요청
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'404':
description: 사용자 정보 없음
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
success: false
error:
code: "USER_NOT_FOUND"
message: "사용자 정보를 찾을 수 없습니다."
details: "해당 사용자가 존재하지 않습니다."
'500':
description: 서버 내부 오류
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
components:
securitySchemes:
BearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
description: "JWT 토큰을 Authorization 헤더에 포함해주세요. (예: Bearer eyJhbGciOiJIUzI1NiIs...)"
schemas:
# Request Schemas
LoginRequest:
type: object
required:
- userId
- password
properties:
userId:
type: string
description: 사용자 ID (고객 식별자)
minLength: 3
maxLength: 20
pattern: '^[a-zA-Z0-9_-]+$'
example: "mvno001"
password:
type: string
description: 사용자 비밀번호
format: password
minLength: 8
maxLength: 50
example: "securePassword123!"
autoLogin:
type: boolean
description: 자동 로그인 옵션 (true 시 24시간 세션 유지)
default: false
example: false
RefreshTokenRequest:
type: object
required:
- refreshToken
properties:
refreshToken:
type: string
description: JWT Refresh Token
example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
PermissionCheckRequest:
type: object
required:
- serviceType
properties:
serviceType:
type: string
description: 확인하려는 서비스 타입
enum:
- BILL_INQUIRY
- PRODUCT_CHANGE
example: "BILL_INQUIRY"
# Response Schemas
LoginResponse:
type: object
properties:
success:
type: boolean
description: 응답 성공 여부
example: true
message:
type: string
description: 응답 메시지
example: "로그인이 성공적으로 완료되었습니다."
data:
type: object
properties:
accessToken:
type: string
description: JWT Access Token (30분 만료)
example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
refreshToken:
type: string
description: JWT Refresh Token (24시간 만료)
example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
expiresIn:
type: integer
description: Access Token 만료까지 남은 시간 (초)
example: 1800
user:
$ref: '#/components/schemas/UserInfo'
TokenVerifyResponse:
type: object
properties:
success:
type: boolean
example: true
message:
type: string
example: "토큰이 유효합니다."
data:
type: object
properties:
valid:
type: boolean
description: 토큰 유효성
example: true
user:
$ref: '#/components/schemas/UserInfo'
expiresIn:
type: integer
description: 토큰 만료까지 남은 시간 (초)
example: 1200
RefreshTokenResponse:
type: object
properties:
success:
type: boolean
example: true
message:
type: string
example: "토큰이 성공적으로 갱신되었습니다."
data:
type: object
properties:
accessToken:
type: string
description: 새로 발급된 JWT Access Token
example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
expiresIn:
type: integer
description: 새 토큰 만료까지 남은 시간 (초)
example: 1800
PermissionsResponse:
type: object
properties:
success:
type: boolean
example: true
message:
type: string
example: "권한 정보를 성공적으로 조회했습니다."
data:
type: object
properties:
userId:
type: string
example: "mvno001"
permissions:
type: array
items:
$ref: '#/components/schemas/Permission'
PermissionCheckResponse:
type: object
properties:
success:
type: boolean
example: true
message:
type: string
example: "서비스 접근 권한이 확인되었습니다."
data:
type: object
properties:
serviceType:
type: string
example: "BILL_INQUIRY"
hasPermission:
type: boolean
example: true
permissionDetails:
$ref: '#/components/schemas/Permission'
UserInfoResponse:
type: object
properties:
success:
type: boolean
example: true
message:
type: string
example: "사용자 정보를 성공적으로 조회했습니다."
data:
$ref: '#/components/schemas/UserInfoDetail'
SuccessResponse:
type: object
properties:
success:
type: boolean
example: true
message:
type: string
example: "요청이 성공적으로 처리되었습니다."
ErrorResponse:
type: object
properties:
success:
type: boolean
example: false
error:
type: object
properties:
code:
type: string
description: 오류 코드
example: "AUTH_001"
message:
type: string
description: 사용자에게 표시될 오류 메시지
example: "ID 또는 비밀번호를 확인해주세요"
details:
type: string
description: 상세 오류 정보
example: "입력된 인증 정보가 올바르지 않습니다."
timestamp:
type: string
format: date-time
description: 오류 발생 시간
example: "2024-01-15T10:30:00Z"
# Common Schemas
UserInfo:
type: object
properties:
userId:
type: string
description: 사용자 ID
example: "mvno001"
userName:
type: string
description: 사용자 이름
example: "홍길동"
phoneNumber:
type: string
description: 휴대폰 번호
example: "010-1234-5678"
permissions:
type: array
description: 사용자 권한 목록
items:
type: string
enum:
- BILL_INQUIRY
- PRODUCT_CHANGE
example:
- "BILL_INQUIRY"
- "PRODUCT_CHANGE"
UserInfoDetail:
type: object
properties:
userId:
type: string
example: "mvno001"
userName:
type: string
example: "홍길동"
phoneNumber:
type: string
example: "010-1234-5678"
email:
type: string
format: email
example: "hong@example.com"
status:
type: string
enum:
- ACTIVE
- INACTIVE
- LOCKED
example: "ACTIVE"
lastLoginAt:
type: string
format: date-time
description: 마지막 로그인 시간
example: "2024-01-15T09:30:00Z"
permissions:
type: array
items:
type: string
example:
- "BILL_INQUIRY"
- "PRODUCT_CHANGE"
Permission:
type: object
properties:
permission:
type: string
enum:
- BILL_INQUIRY
- PRODUCT_CHANGE
example: "BILL_INQUIRY"
description:
type: string
example: "요금 조회 서비스"
granted:
type: boolean
example: true
# API 오류 코드 정의
# AUTH_001: 잘못된 인증 정보
# AUTH_002: 계정 잠금 (5회 실패)
# AUTH_003: 계정 일시 잠금
# TOKEN_EXPIRED: 토큰 만료
# TOKEN_INVALID: 유효하지 않은 토큰
# REFRESH_TOKEN_INVALID: Refresh Token 무효
# USER_NOT_FOUND: 사용자 정보 없음
# UNAUTHORIZED: 인증 필요
# VALIDATION_ERROR: 입력 데이터 검증 오류
# INTERNAL_SERVER_ERROR: 서버 내부 오류