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: 서버 내부 오류