Auth API 엔드포인트
목차
- 관련 문서
- 접근 권한 매트릭스
- 앱 인증 API
- Access Code 검증 API
- 약관 관리 API
- 게스트 인증 API
- 인증 핵심 기능 API
- GesundheitsID 인증 및 연동
- 내부 운영자 인증 API
- 오류 코드
- 변경 이력
관련 문서
- Agreements 도메인 엔드포인트: /domains/common/core-domains/agreements/endpoints
- Access Code 도메인 엔드포인트: /domains/common/supporting-domains/access-code/endpoints
접근 권한 매트릭스
| 엔드포인트 | System Admin | IAM Admin | Service Account | Regular User | 비고 |
|---|---|---|---|---|---|
| POST /v1/auth/app/challenge | ✓ | ✓ | ✓ | ✓ | |
| POST /v1/auth/app/complete-challenge | ✓ | ✓ | ✓ | ✓ | |
| POST /v1/auth/access-code/validate | ✓ | ✓ | ✓ | ✓ | |
| GET /v1/auth/agreements | ✓ | ✓ | ✓ | ✓ | App/Access Token |
| POST /v1/auth/email/verification-code | ✓ | ✓ | ✓ | ✓ | |
| POST /v1/auth/email/verify | ✓ | ✓ | ✓ | ✓ | |
| POST /v1/auth/register | ✓ | ✓ | ✓ | ✓ | |
| POST /v1/auth/guest/register | ✓ | ✓ | ✓ | ✓ | App Token, identityLevel=guest 발급 |
| POST /v1/auth/guest/link/init | ✓ | ✓ | ✓ | ✓ | 게스트 토큰 + 동일 디바이스 필요 |
| POST /v1/auth/guest/link/complete | ✓ | ✓ | ✓ | ✓ | Step-up 인증 완료, 게스트 토큰 필수 |
| POST /v1/auth/guest/link/cancel | ✓ | ✓ | ✓ | ✓ | 진행 중인 Step-up 세션 취소 |
| POST /v1/auth/login | ✓ | ✓ | ✓ | ✓ | |
| POST /v1/auth/token/refresh | ✓ | ✓ | ✓ | ✓ | |
| POST /v1/auth/token/validate | ✓ | ✓ | ✓ | ✓ | |
| POST /v1/auth/logout | ✓ | ✓ | ✓ | ✓ | |
| POST /v1/auth/2fa/enable | ✓ | ✓ | ✘ | ✓ | |
| POST /v1/auth/2fa/verify | ✓ | ✓ | ✘ | ✓ | |
| POST /v1/auth/2fa/disable | ✓ | ✓ | ✘ | ✓ | |
| POST /v1/auth/token/anonymous | ✓ | ✓ | ✓ | ✓ | appToken 필수(익명 토큰 발급), 기본 클레임 tier=anonymous/allowed_domains/data_persistence |
| GET /v1/auth/sessions | ✓ | ✓ | ✓ | 자신만 | |
DELETE /v1/auth/sessions/{id} | ✓ | ✓ | ✘ | 자신만 | |
| POST /v1/auth/gesundheitsid/link/initialize | ✓ | ✓ | ✘ | 자신만 | |
| POST /v1/auth/gesundheitsid/link/complete | ✓ | ✓ | ✘ | 자신만 | |
| GET /v1/auth/gesundheitsid/link | ✓ | ✓ | ✘ | 자신만 | |
| POST /v1/auth/gesundheitsid/unlink | ✓ | ✓ | ✘ | 자신만 | |
| POST /v1/auth/gesundheitsid/login/initialize | ✓ | ✓ | ✘ | ✓ | |
| POST /v1/auth/gesundheitsid/login/complete | ✓ | ✓ | ✘ | ✓ | |
| POST /v1/auth/operation/register | ✓ | ✓ | ✘ | ✘ | 내부 운영자 등록 |
DELETE /v1/auth/operation/user/{id} | ✓ | ✘ | ✘ | ✘ | 내부 운영자 삭제 |
| POST /v1/auth/operation/login | ✓ | ✓ | ✓ | ✘ | 내부 운영자 로그인 |
| POST /v1/auth/operation/logout | ✓ | ✓ | ✓ | ✘ | 내부 운영자 로그아웃 |
| POST /v1/auth/operation/email/verification-code | ✓ | ✓ | ✓ | ✘ | 내부 운영자 이메일 인증 |
| POST /v1/auth/operation/email/verify | ✓ | ✓ | ✓ | ✘ | 내부 운영자 이메일 확인 |
| POST /v1/auth/operation/token/refresh | ✓ | ✓ | ✓ | ✘ | 내부 운영자 토큰 갱신 |
| POST /v1/auth/operation/token/validate | ✓ | ✓ | ✓ | ✘ | 내부 운영자 토큰 검증 |
| DELETE /v1/auth/registration/abort | ✓ | ✓ | ✓ | ✓ | 등록 프로세스 중단 및 캐시 정리 |
| DELETE /v1/auth/me | ✓ | ✓ | ✓ | 자신만 | 계정 비활성화 (탈퇴) |
참고:
- ✓: 접근 가능
- ✘: 접근 불가
- (범위 내): 할당된 조직/팀 범위 내에서만 접근 가능
- (자신만): 자신의 데이터에만 접근 가능
- App/Access Token: 앱 토큰 또는 사용자 액세스 토큰으로 접근 가능
앱 인증 API
📌 기술 구현 문서: 앱 인증 시스템 구현 | 앱 보안 인증 시스템 기술 명세
📌 인증 방식: 앱 인증은 챌린지-응답 메커니즘을 통해 앱 무결성을 검증하는 보안 인증 방식입니다. 핵심 엔드포인트는 challenge(챌린지 요청), complete-challenge(챌린지 완료 및 토큰 발급), verify-token(토큰 검증) 입니다.
앱 보안 인증 API는 모바일 앱과 백엔드 서버 간의 안전한 통신을 위한 인증 메커니즘을 제공합니다. 이 API는 Device ID 기반 인증과 시간 기반 OTP 챌린지 인증을 통해 클라이언트 앱의 무결성을 검증합니다.
중요: 앱 인증 프로세스는 사용자 로그인 전에 가장 먼저 수행되어야 하는 단계입니다. 앱 인증을 통해 발급받은
appToken은 사용자가 로그인하여accessToken을 발급받기 전까지 필요한 모든 API 호출(Access Code 검증, 약관 조회/동의, 이메일 인증, 회원가입, 로그인 등)에 사용됩니다. 이는 유효한 클라이언트 앱에서의 요청임을 먼저 검증한 후, 사용자 인증을 진행하는 2단계 인증 구조입니다.
참고: 본 문서에 포함된 모든 시간 관련 필드는 13자리 Unix 타임스탬프(밀리초) 형식을 사용합니다. 시간 데이터 처리에 대한 자세한 내용은 데이터 타입 가이드라인을 참조하세요.
앱 인증 토큰 사용 흐름
-
앱 보안 인증
- 디바이스 정보를 이용해 챌린지 요청(
/auth/app/challenge) 및 응답(/auth/app/complete-challenge)을 통해 appToken 발급 - 모든 API 요청에
Authorization: Bearer {appToken}헤더 포함
- 디바이스 정보를 이용해 챌린지 요청(
-
사용자 미인증 상태에서의 API 호출
- 앱 토큰만으로 접근 가능한 모든 API에 appToken 사용
- 예시: Access Code 검증, 약관 목록 조회 및 동의, 이메일 인증, 회원가입, 로그인 등
ℹ️ 참고: 사용자 인증 후 토큰 사용
- 로그인 성공 시 accessToken과 refreshToken 발급
- 이후 사용자 인증이 필요한 API는
Authorization: Bearer {accessToken}헤더 사용- accessToken 만료 시 refreshToken을 이용해 새로운 토큰 발급
- 자세한 내용은 사용자 인증 섹션 참조
2.1 앱 보안 인증 API
2.1.1 챌린지 요청 API
- HTTP 메서드: POST
- 경로: /auth/app/challenge
- Content-Type: application/json
요청 (Request)
{
"deviceId": {
"uuid": "e8e53358-c282-4f69-89c4-bae40f0b7587",
"platform": "iOS",
"version": "1.2.0",
"timestamp": 1714523700000
},
"deviceIdHash": "AMoVT5hzKN9A3OCz_PjZdzEJYZL5K8pY0FpOYgpKa7U"
}
참고:
deviceIdHash는deviceId객체를 JSON 문자열로 직렬화한 후 SHA-256 해싱하여 생성합니다. 해시 생성 시 앱 시크릿을 솔트로 추가하여 보안을 강화합니다.(Salt는 추후에 개발 예정)
응답 (Response)
- 성공 응답 (200 OK)
{
"challenge": "randomBase64EncodedChallengeString",
"serverTimestamp": 1714523700000,
"nonce": "randomNonceString"
}
- 오류 응답 (400 Bad Request)
{
"code": 2009,
"message": "INVALID_DEVICE_ID",
"detail": "디바이스 ID가 유효하지 않습니다."
}
2.1.2 챌린지 완료 및 토큰 발급 API
- HTTP 메서드: POST
- 경로: /auth/app/complete-challenge
- Content-Type: application/json
요청 (Request)
{
"hashedDeviceId": "f1a23bc45d67e8f9a0b1c2d3e4f56a78b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4",
"encryptedChallenge": "base64EncodedEncryptedChallenge",
"nonce": "nonceFromPreviousRequest"
}
참고:
encryptedChallenge는 챌린지 응답 API에서 받은 챌린지 문자열을 시간 기반 암호화 키로 암호화한 것입니다. 암호화 키는 타임윈도우, 디바이스 ID 해시, 논스를 조합하여 생성합니다.
응답 (Response)
- 성공 응답 (200 OK)
{
"appToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"appSecret": "secureRandomAppSecretString",
"expiresAt": 1714527300000,
"device": {
"id": "device_123",
"uuid": "e8e53358-c282-4f69-89c4-bae40f0b7587",
"platform": "iOS",
"version": "1.2.0",
"createdAt": 1714523700000,
"lastActiveAt": 1714523700000
}
}
- 오류 응답 (401 Unauthorized)
{
"code": 2010,
"message": "CHALLENGE_VERIFICATION_FAILED",
"detail": "챌린지 검증에 실패했습니다."
}
2.2 사용자 인증
2.2.1 로그인 API
- HTTP 메서드: POST
- 경로: /auth/login
- Headers:
- Content-Type: application/json
- Authorization: Bearer
{appToken}
응답 (Response)
- 성공 응답 (200 OK)
{
"tokens": [
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"type": "ACCESS_TOKEN",
"expiresIn": 1800,
"issuedAt": 1714523700000
},
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"type": "REFRESH_TOKEN",
"expiresIn": 604800,
"issuedAt": 1714523700000
}
],
"user": {
"id": "c1e7c5cf-6fc2-4f4f-8e2f-9b8a3c5e2d1b",
"email": "user@example.com",
"questionnaireBundleId": "be7c5cf-6fc2-4f4f-8e2f-9b85e2d1b",
"suspensionEndAt": 1968886400000,
"createdAt": 1678886400000
},
"userCycle": {
"id": "d2f8e5c1-9b3a-4f8c-8e2f-1b9a3c5e2d7c",
"status": "ACTIVE",
"startedAt": 1678886400000,
"count": 3
},
"profile": {
"language": "de-DE",
"timezone": {
"id": "Europe/Berlin",
"offsetInMinutes": 60
},
"userName": "John Doe"
},
"roles": ["healthcare.patientUser", "content.editor"],
"permissions": ["sleep.log.create", "sleep.log.read", "sleep.goal.update"],
"agreements": [
{
"versionId": "some-version-id",
"type": "CONSENT",
"isRequired": false,
"title": "Datenverarbeitung zur Verbesserung",
"text": "Ich erlaube die Verarbeitung der zu der dauerhaften Gewährleistung der technischen Funktionsfähigkeit...",
"detailsUrl": "https://www.weltcorp.com",
"isAgreed": false,
"agreedAt": 1699981800000
}
]
}
2.2.2 사용자 정보 조회 API
- HTTP 메서드: GET
- 경로: /auth/user/
{userId} - Headers:
- Authorization: Bearer
{accessToken}
- Authorization: Bearer
응답 예시
{
"id": "user_123",
"email": "user@example.com",
"name": "홍길동",
"status": "ACTIVE",
"createdAt": 1711929600000,
"updatedAt": 1714523700000,
"lastLoginAt": 1714524600000
}
2.3 역할 관리 API
- HTTP 메서드: GET
- 경로: /auth/roles
- Headers:
- Authorization: Bearer
{accessToken}
- Authorization: Bearer
응답 예시
{
"items": [
{
"id": "role_123",
"name": "ADMIN",
"description": "시스템 관리자",
"permissions": ["USER_MANAGE", "ROLE_MANAGE"],
"createdAt": 1711929600000,
"updatedAt": 1714523700000
}
],
"total": 1,
"page": 1,
"pageSize": 10,
"totalPages": 1
}
2.4 사용자 역할 할당 API
- HTTP 메서드: POST
- 경로: /auth/user/
{userId}/roles - Headers:
- Authorization: Bearer
{accessToken} - Content-Type: application/json
- Authorization: Bearer
요청 (Request)
{
"roleIds": ["role_123"]
}
응답 (Response)
- 성공 응답 (200 OK)
{
"userId": "user_123",
"roleIds": ["role_123"],
"assignedAt": 1714524600000,
"assignedBy": "admin_456"
}
Access Code 검증 API
📌 기술 구현 문서: Access Code 검증 구현 가이드 | Access Code 명세
📌 구현 순서: 앱 보안 인증 API 다음으로 구현해야 하는 중요한 기능입니다.
Access Code는 회원가입 시 사용자의 접근 권한을 검증하는 데 사용되는 16자리 코드입니다. 회원가입 전에 반드시 유효한 Access Code 검증이 필요합니다.
3.1 Access Code 검증 API
- HTTP 메서드: POST
- 경로: /auth/access-code/validate
- Headers:
- Content-Type: application/json
- Authorization: Bearer
{appToken}
요청 (Request)
{
"accessCode": "ABCD1234EFGH5678"
}
응답 (Response)
- 성공 응답 (200 OK)
{
"valid": true, // 검증 성공
"accessCodeId": "ac_123",
"expiresAt": 1718949600000, // Unix Timestamp (milliseconds)
"isUsed": false,
"metadata": {
"remainingAttempts": 10 // 성공 시 남은 시도 횟수 (최대값)
}
}
- 실패 응답 - 유효하지 않은 코드 (400 Bad Request)
{
"code": 3001,
"message": "INVALID_ACCESS_CODE",
"detail": "유효하지 않은 Access Code입니다.",
"metadata": {
"remainingAttempts": 9 // 실패 후 남은 시도 횟수
}
}
- 실패 응답 - 만료된 코드 (400 Bad Request)
{
"code": 3003,
"message": "ACCESS_CODE_EXPIRED",
"detail": "만료된 Access Code입니다.",
"metadata": {
"remainingAttempts": 9 // 실패 후 남은 시도 횟수
}
}
- 실패 응답 - 이미 사용된 코드 (409 Conflict)
{
"code": 3002,
"message": "ACCESS_CODE_ALREADY_USED",
"detail": "이미 사용된 Access Code입니다.",
"metadata": {
"remainingAttempts": 9 // 실패 후 남은 시도 횟수
}
}
- 실패 응답 - 시도 횟수 제한 초과 (429 Too Many Requests)
{
"code": 3007,
"message": "CODE_BLOCKED",
"detail": "잘못된 시도 횟수 초과로 인해 Access Code가 일시적으로 차단되었습니다.",
"metadata": {
"remainingLockoutSeconds": 3599 // 잠금 해제까지 남은 시간(초)
}
}
약관 관리 API
📌 기술 구현 문서: 약관 관리 구현 가이드 | 약관 관리 명세
📌 구현 순서: Access Code 검증 API 다음으로 구현해야 하는 기능입니다.
📌 참고: 약관 관리 프로세스에 대한 자세한 내용은 약관 도메인 엔드포인트 문서를 참조하세요.
경로 변경 안내: 약관 관리 API는 기존
/auth/terms에서/terms로 경로가 변경되었습니다.
약관 관리 API는 서비스 이용을 위한 약관의 버전 관리와 활성화 상태 관리, 다국어 지원 및 사용자 동의 이력 관리를 담당합니다.
게스트/익명 인증 API
📌 설계 문서: 게스트 Step-up 인증 및
identityLevel확장 설계📌 요약: 게스트/익명 인증 API는 약관 동의만으로 제한 계정(guest 또는 anonymous)을 생성하고, 추가 인증을 통해 정규 사용자로 승격시키는 Step-up 플로우를 제공합니다. 모든 요청은 App Token 검증을 통과한 디바이스에서 호출되어야 하며, 게스트/익명 토큰은 제한된 allowed_domains·data_persistence를 가진다.
4.0 익명 토큰 발급
- HTTP 메서드:
POST - 경로:
/v1/auth/token/anonymous(정책에 맞게 조정 가능) - 인증:
Authorization: Bearer {appToken} - 설명: 사용자가 로그인하지 않은 상태에서 anonymous tier 토큰을 발급한다. 기본 클레임은
tier=anonymous,allowed_domains=["faq","docs-public","agent-help-public"],data_persistence=none을 강제한다. - 제약:
- appToken 필수(어떤 앱이든 유효한 appToken이면 가능), 사용자 인증 토큰 없이 호출
- 발급된 토큰은 FAQ/정보성 에이전트만 호출 가능, 컨텍스트/툴 호출 제한
4.1 게스트 온보딩
- HTTP 메서드:
POST - 경로:
/v1/auth/guest/register - 인증:
Authorization: Bearer {appToken},User-Agent헤더 필수 - 설명: 이메일·소셜 인증 없이 필수 약관 동의와 디바이스 검증만으로
identityLevel=guest토큰 페어를 발급합니다.region은 User-Agent 헤더의locale에서 자동 추출됩니다.
요청 헤더
Authorization: Bearer {appToken}
User-Agent: dha-sleep-app/2.3.1 (iOS; iOS 17.5; iPhone14,5; AppStore; locale/ko-KR)
Content-Type: application/json
User-Agent에서 자동 추출되는 정보:
region: locale에서 추출 (예:locale/ko-KR→region: "KR")platform,appVersion,osVersion,model: 디바이스 정보
요청 본문
{
"agreements": [
{ "versionId": "9f8a7b6c-5d4e-3f2g-1h0i-j9k8l7m6n5o4", "isAgreed": true },
{ "versionId": "marketing-agreement-version-id", "isAgreed": false }
],
"deviceUuid": "e8e53358-c282-4f69-89c4-bae40f0b7587",
"profile": {
"language": "ko-KR",
"timezone": {
"id": "Asia/Seoul",
"offsetInMinutes": 540
}
}
}
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
agreements | array | Yes | 약관 동의 목록 (정식 회원가입과 동일한 형식) |
deviceUuid | string | Yes | 디바이스 UUID (앱 인증 API의 deviceId.uuid와 동일한 값 사용) |
profile | object | Yes | 사용자 프로필 정보 (정식 회원가입과 동일한 형식) |
profile.language | string | Yes | 사용자 언어 설정 (예: ko-KR, de-DE) |
profile.timezone.id | string | Yes | 시간대 ID (예: Asia/Seoul, Europe/Berlin) |
profile.timezone.offsetInMinutes | number | Yes | UTC 기준 오프셋 (분 단위) |
응답 본문 (201 Created)
{
"success": true
}
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
success | boolean | Yes | 게스트 등록 성공 여부 |
ℹ️ 참고: 게스트 등록 API는 계정 생성만 수행합니다. 토큰 발급 및 상세 정보(
user,userCycle,profile등)는 게스트 로그인 API를 통해 획득합니다.
⚠️ 제약
- 게스트 등록 후 별도의 로그인 API(
/v1/auth/guest/login)를 통해 토큰을 받아야 합니다.- 게스트 토큰은 최초 디바이스에만 유효합니다. 다른 디바이스에서 사용하려면 Step-up 후 재로그인해야 합니다.
- 필수 약관 미동의 시
1103 REQUIRED_TERMS_NOT_AGREED오류가 반환됩니다.
4.1.1 게스트 로그인
- HTTP 메서드:
POST - 경로:
/v1/auth/guest/login - 인증:
Authorization: Bearer {appToken},User-Agent헤더 필수 - 설명: 이미 등록된 게스트 계정으로 다시 로그인합니다. 디바이스 UUID로 기존 게스트 계정을 식별하고 새로운 토큰 페어를 발급합니다.
요청 헤더
Authorization: Bearer {appToken}
User-Agent: dha-sleep-app/2.3.1 (iOS; iOS 17.5; iPhone14,5; AppStore; locale/ko-KR)
Content-Type: application/json
요청 본문
{
"deviceUuid": "e8e53358-c282-4f69-89c4-bae40f0b7587"
}
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
deviceUuid | string | Yes | 디바이스 UUID (게스트 등록 시 사용한 것과 동일한 값) |
응답 본문 (200 OK)
{
"tokens": [
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"type": "ACCESS_TOKEN",
"expiresIn": 900,
"issuedAt": 1714523700000
},
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"type": "REFRESH_TOKEN",
"expiresIn": 43200,
"issuedAt": 1714523700000
}
],
"user": {
"id": "guest_a1b2c3d4-5e6f-7g8h-9i0j-k1l2m3n4o5p6",
"identityLevel": "GUEST",
"createdAt": 1714520000000,
"expiresAt": 1714606400000
},
"userCycle": {
"id": "d2f8e5c1-9b3a-4f8c-8e2f-1b9a3c5e2d7c",
"status": "ACTIVE",
"startedAt": 1714520000000,
"count": 1,
"treatmentDurationDays": -1
},
"profile": {
"language": "ko-KR",
"timezone": {
"id": "Asia/Seoul",
"offsetInMinutes": 540
}
},
"roles": ["guest.user"],
"permissions": ["sleep.log.create", "sleep.log.read", "content.read"],
"agreements": [...]
}
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
tokens | array | Yes | 인증 토큰 배열 (access + refresh) |
user | object | Yes | 사용자(게스트) 계정 정보 |
user.id | string | Yes | 사용자 계정 ID |
user.identityLevel | string | Yes | 신원 수준 (게스트의 경우 GUEST) |
user.createdAt | number | Yes | 계정 생성일 (Unix timestamp ms) |
user.expiresAt | number | No | 세션 만료일 (Unix timestamp ms) |
userCycle | object | Yes | 사용자 주기 정보 (정식 사용자 login API의 userCycle과 동일) |
userCycle.id | string | Yes | 사용자 주기 ID |
userCycle.status | string | Yes | 주기 상태 (ACTIVE) |
userCycle.startedAt | number | Yes | 주기 시작일 (Unix timestamp ms) |
userCycle.count | number | Yes | 총 주기 개수 (게스트는 항상 1) |
userCycle.treatmentDurationDays | number | Yes | 치료 기간 (일수). 게스트는 -1 (무제한) |
profile | object | Yes | 프로필 정보 |
roles | string[] | Yes | 역할 목록 |
permissions | string[] | Yes | 권한 목록 |
agreements | array | Yes | 동의한 약관 목록 |
⚠️ 오류 응답
404 GUEST_ACCOUNT_NOT_FOUND: 해당 디바이스로 등록된 게스트 계정이 없음410 GUEST_ACCOUNT_EXPIRED: 게스트 계정이 만료됨410 GUEST_ACCOUNT_UPGRADED: 정식 사용자로 업그레이드되어 게스트 로그인 불가
4.2 Step-up 인증 준비
- HTTP 메서드:
POST - 경로:
/v1/auth/guest/link/init - 인증:
Authorization: Bearer {guestAccessToken} - 설명: 이메일/Kakao 등 외부 자격 연동을 위한 임시
linkSessionId를 발급합니다. 게스트 토큰과 동일한 디바이스에서만 호출 가능합니다.
요청 본문
{
"method": "email",
"payload": {
"email": "guest@example.com",
"language": "de"
}
}
응답 본문 (200 OK)
{
"linkSessionId": "link_01J4V0JMSF8PM6N2V2SVS1ZPAB",
"expiresAt": 1724300400000,
"method": "email"
}
⚠️ 제약
- 동일 디바이스 검증 실패 시
401 DEVICE_MISMATCH_FOR_STEP_UP오류를 반환합니다.- 이미 정규 사용자로 승격된 계정은
409 IDENTITY_LEVEL_ALREADY_REGISTERED오류가 발생합니다.
4.3 Step-up 인증 완료
- HTTP 메서드:
POST - 경로:
/v1/auth/guest/link/complete - 인증:
Authorization: Bearer {guestRefreshToken}(Refresh Token 기반 재발급 요청) - 설명:
linkSessionId와 외부 자격 증명을 검증하고, 새로운identityLevel=registered토큰을 발급합니다. 성공 시 기존 게스트 토큰은 블랙리스트 처리됩니다.
요청 본문
{
"linkSessionId": "link_01J4V0JMSF8PM6N2V2SVS1ZPAB",
"verificationCode": "483912"
}
응답 본문 (200 OK)
{
"accessToken": "eyJhbGciOi...",
"refreshToken": "def50200...",
"identityLevel": "registered",
"bindings": [
{
"type": "email",
"value": "guest@example.com"
}
],
"revokedRefreshTokens": ["def501ab-guest-refresh"]
}
⚠️ 정책
- 검증 실패 3회 초과 시 게스트 계정이
LOCKED상태로 전환되고429 STEP_UP_TOO_MANY_ATTEMPTS가 반환됩니다.- 성공 시
auth.identity-level.changed이벤트가 발행되어 IAM/Plan/Group이 재계산을 수행합니다.
4.4 Step-up 인증 취소
- HTTP 메서드:
POST - 경로:
/v1/auth/guest/link/cancel - 인증:
Authorization: Bearer {guestAccessToken} - 설명: 진행 중인 Step-up 세션을 취소하고 이메일·OAuth 임시 리소스를 정리합니다.
요청 본문
{
"linkSessionId": "link_01J4V0JMSF8PM6N2V2SVS1ZPAB",
"reason": "USER_ABORT"
}
응답 본문 (204 No Content)
⚠️ 정책
- 이미 완료되었거나 만료된 세션을 취소하면
404 LINK_SESSION_NOT_FOUND가 반환됩니다.- 취소 후 재시도 시 새로운
linkSessionId를 발급해야 합니다.
인증 핵심 기능 API
📌 구현 순서: 앱 보안 인증, Access Code 검증, 약관 관리 API가 구현된 후 진행합니다.
5.1 회원가입 프로세스
📌 기술 구현 문서: 회원가입 프로세스 구현 가이드 | 인증 서비스 명세
회원가입은 다음과 같은 단계로 진행됩니다:
- Access Code 검증: 사용자가 제출한 16자리 Access Code의 유효성을 검증합니다.
- 통합 동의 항목 조회: 회원가입 화면에 표시해야 할 필수 약관 및 선택적 동의 항목 목록과 상태를 조회합니다.
- 이메일 인증 코드 발송: 사용자가 입력한 이메일로 6자리 OTP 번호를 발송합니다.
- 이메일 인증 코드 확인: 사용자가 수신한 OTP 번호를 입력하여 이메일 유효성을 검증합니다.
- 회원가입: Access Code 검증, 약관 동의 정보와 인증이 완료된 이메일을 사용하여 회원가입을 진행합니다. 이때 클라이언트는 동의한 약관 정보를 함께 전달합니다.
중요: 회원가입 프로세스의 모든 단계는 사용자 로그인 전이므로, 각 API 호출 시 앱 보안 인증을 통해 발급받은
appToken을Authorization: Bearer {appToken}헤더에 포함하여 요청해야 합니다. 회원가입 성공 후에는 사용자accessToken이 발급되며, 이후 API 호출에는 이 accessToken을 사용합니다.
중요: 회원가입은 반드시 유효한 Access Code 검증과 이메일 인증이 모두 완료된 후에만 가능하며, 필수 약관 동의 정보가 포함되어야 합니다. 필수 약관에 동의하지 않은 경우
REQUIRED_TERMS_NOT_AGREED오류가 발생하며, 인증되지 않은 이메일로 회원가입을 시도할 경우EMAIL_NOT_VERIFIED오류가 발생합니다. 이메일 인증은 완료 후 30분 동안 유효합니다.
5.1.1 약관 목록 조회
- HTTP 메서드: GET
- 경로: /terms
- Headers:
- Accept-Language: ko-KR (기본값) 또는 en-US
- Authorization: Bearer
{appToken 또는 accessToken}
- 용도: 시스템의 모든 약관(활성/비활성 등)을 필터링하여 목록 형태로 조회할 때 사용합니다. (예: 관리자 페이지) 또는 특정 타입의 약관만 필요할 때 사용합니다. 회원가입/온보딩 등 여러 종류의 동의 항목이 필요한 화면에는 아래의 통합 동의 항목 조회 API 사용을 권장합니다.
응답 (Response)
- 성공 응답 (200 OK): (기존과 동일)
{
"terms": [
{
"id": 1,
"version": "1.0.0",
"title": "서비스 이용약관",
"type": "service",
"required": true,
"createdAt": 1672531200000,
"updatedAt": 1672531200000
}
]
}
5.1.2 통합 동의 항목 조회
- HTTP 메서드: GET
- 경로: /v1/auth/agreements
- Headers:
- Accept-Language: de-DE (기본값), ko-KR, en-US 등
- Authorization: Bearer
{appToken 또는 accessToken}
- 용도: 특정 시점(예: 회원가입, 로그인 후 첫 진입)에 사용자에게 반드시 제시하고 동의받아야 하는 필수 약관과 함께 표시될 선택적 동의 항목 및 해당 사용자의 현재 동의 상태를 한 번에 조회하기 위해 사용됩니다. 클라이언트는 이 응답을 사용하여 독일어 예시 이미지와 같은 동의 화면을 구성할 수 있습니다.
참고: 이 API는 약관 도메인과 동의 도메인의 정보를 통합적으로 제공하지만, 주로 인증 프로세스(회원가입, 로그인)에서 사용되므로 인증 도메인 아래에 위치합니다. 약관 및 동의 관련 자세한 정보는 각각 Agreements 도메인 문서를 참조하세요.
응답 (Response)
- 성공 응답 (200 OK):
{
"items": [
{
"type": "TERMS", // 약관 항목
"key": "9f8a7b6c-5d4e-3f2g-1h0i-j9k8l7m6n5o4", // 약관 버전 ID (UUID 형식)
"isRequired": true, // 필수 동의 여부
"version": "1.0.0", // 현재 활성 버전
"title": "Nutzungsbedingungen", // 약관 이름 번역 (agreements_translations.name)
"text": "Ich stimme den Nutzungsbedingungen gemäß der DiGAV § 4 Abs. 2 zu.", // 현지화된 텍스트
"detailsUrl": "/terms/service/v1.0.0/de", // 약관 상세 내용 URL
"orderIndex": 0, // UI 표시 순서 (0부터 시작, 작은 값이 먼저 표시)
"isAgreed": false // 사용자의 이 버전에 대한 동의 여부 (기존 동의 이력 기반)
},
{
"type": "CONSENT", // 선택적 동의 항목
"key": "DATA_PROCESSING_IMPROVEMENT", // 동의 목적 키 (ConsentPurpose)
"isRequired": false,
"title": "Datenverarbeitung zur Verbesserung", // 동의 항목 이름 번역
"text": "Ich erlaube die Verarbeitung der zu der dauerhaften Gewährleistung der technischen Funktionsfähigkeit...", // 현지화된 텍스트
"detailsUrl": null, // 관련 설명 페이지 URL (선택적)
"orderIndex": 1, // UI 표시 순서 (0부터 시작, 작은 값이 먼저 표시)
"isAgreed": false // 사용자의 현재 이 목적에 대한 동의 상태 (Consent 정보 기반)
}
// ... 다른 필요한 약관 또는 동의 항목 추가 ...
]
}
- 오류 응답 (401 Unauthorized - 토큰 오류)
{
"code": 2050,
"message": "INVALID_APP_TOKEN",
"detail": "토큰이 유효하지 않습니다"
}
- 오류 응답 (500 Internal Server Error)
{
"code": 2000,
"message": "SERVER_ERROR",
"detail": "서버 내부 오류"
}
5.1.3 이메일 인증 코드 발송
- HTTP 메서드: POST
- 경로: /auth/email/verification-code
- Headers:
- Content-Type: application/json
- Authorization: Bearer
{appToken}
중요: 이 API는 회원가입, 로그인, 비밀번호 재설정 등 다양한 인증 프로세스에서 사용되는 이메일 인증 코드 발송 API입니다. 인증 목적에 따라
type필드를 사용합니다. REGISTRATION 타입의 경우 이미 가입된 이메일에 대해 오류가 반환됩니다.
요청 (Request)
{
"email": "user@example.com",
"type": "REGISTRATION"
}
| 필드 | 타입 | 설명 | 필수 |
|---|---|---|---|
email | string | 인증 코드를 받을 이메일 주소 | Yes |
type | string | 이메일 인증 타입 (REGISTRATION, LOGIN, PASSWORD_RESET) | Yes |
응답 (Response)
- 성공 응답 (200 OK)
{
"success": true,
"email": "user@example.com",
"expiresIn": 300,
"requestId": "req_123",
"type": "REGISTRATION"
}
- 오류 응답 (409 Conflict - 이미 가입된 이메일, type이 REGISTRATION인 경우)
{
"code": 2020,
"message": "EMAIL_ALREADY_EXISTS",
"detail": "이미 사용 중인 이메일입니다."
}
- 오류 응답 (422 Unprocessable Entity - 가입되지 않은 이메일, type이 LOGIN 또는 PASSWORD_RESET인 경우)
{
"code": 2027,
"message": "UNREGISTERED_EMAIL",
"detail": "가입되지 않은 이메일입니다."
}
참고: 이메일로 6자리 OTP 번호가 발송됩니다.
expiresIn은 인증 코드의 유효 시간(초)입니다.requestId는 인증 코드 확인 시 필요합니다.
5.1.4 이메일 인증 코드 확인
- HTTP 메서드: POST
- 경로: /auth/email/verify
- Headers:
- Content-Type: application/json
- Authorization: Bearer
{appToken}
요청 (Request)
{
"email": "user@example.com",
"code": "123456",
"requestId": "req_123",
"type": "REGISTRATION"
}
| 필드 | 타입 | 설명 | 필수 |
|---|---|---|---|
email | string | 인증 코드를 받은 이메일 주소 | Yes |
code | string | 이메일로 받은 6자리 인증 코드 | Yes |
requestId | string | 코드 발송 요청 시 받은 요청 ID | Yes |
type | string | 이메일 인증 타입 (REGISTRATION, LOGIN, PASSWORD_RESET) | Yes |
응답 (Response)
- 성공 응답 (200 OK)
{
"valid": true,
"email": "user@example.com",
"verificationId": "ver_456",
"verifiedAt": 1711011900000,
"expiresAt": 1711013700000,
"type": "REGISTRATION"
}
- 오류 응답 (400 Bad Request - 잘못된 인증 코드)
{
"code": 2017,
"message": "INVALID_VERIFICATION_CODE",
"detail": "잘못된 인증 코드입니다.",
"metadata": {
"remainingAttempts": 9 // 실패 후 남은 시도 횟수
}
}
참고: 인증이 성공하면
verificationId가 발급됩니다. 이 ID는 회원가입 API 호출 시 필요합니다. 인증은expiresAt까지 유효합니다(일반적으로 인증 후 30분). 잘못된 코드 입력 시 최대 10회까지 시도 가능하며, 횟수 초과 시 일정 시간 동안 추가 시도가 제한됩니다.
5.1.5 회원가입 API
- HTTP 메서드: POST
- 경로: /auth/register
- Headers:
- Content-Type: application/json
- Authorization: Bearer
{appToken}
중요: 보안 강화를 위해 사용자 비밀번호는 반드시 클라이언트에서 해시 처리한 후 서버로 전송해야 합니다. 해시는 SHA-256 알고리즘을 사용하며, 솔트로 앱 인증 과정에서 받은
appSecret과 디바이스의 고유 ID(deviceUUID)를 함께 사용합니다. 해시된 비밀번호는passwordHash필드로 전송합니다.
요청 (Request)
{
"email": "user@example.com",
"passwordHash": "base64EncodedPasswordHashString",
"deviceId": "device_123",
"verificationId": "ver_456",
"accessCode": "ABCD1234EFGH5678",
"agreements": [
// 약관 동의 결과
{ "termsId": 1, "version": "1.0.0", "isAgreed": true }
],
"consents": {
// 선택적 동의 결과
"DATA_PROCESSING_IMPROVEMENT": false
},
"profile": {
"language": "de",
"timezone": "Europe/Berlin"
}
}
응답 (Response)
- 성공 응답 (201 Created): (기존과 동일)
{
"userId": "user_123",
"email": "user@example.com",
"profile": {
"language": "ko",
"timezone": "Asia/Seoul"
},
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "eyJhbGciOiJIUzI1NiIs...",
"expiresIn": 3600,
"createdAt": 1711011600000
}
- 오류 응답 (400 Bad Request)
{
"code": 2013,
"message": "EMAIL_NOT_VERIFIED",
"detail": "이메일 인증이 완료되지 않았습니다."
}
- 오류 응답 (400 Bad Request)
{
"code": 1103,
"message": "REQUIRED_TERMS_NOT_AGREED",
"detail": "필수 약관에 동의하지 않았습니다."
}
- 오류 응답 (400 Bad Request)
{
"code": 3010,
"message": "ACCESS_CODE_NOT_VALIDATED",
"detail": "Access Code 검증이 완료되지 않았습니다."
}
- 오류 응답 (400 Bad Request)
{
"code": 2028,
"message": "REGISTRATION_VALIDATION_FAILED",
"detail": "가입 정보 검증에 실패했습니다."
}
- 오류 응답 (409 Conflict)
{
"code": 2020,
"message": "EMAIL_ALREADY_EXISTS",
"detail": "이미 사용 중인 이메일입니다."
}
5.2 로그인 및 인증 프로세스
📌 기술 구현 문서: 로그인 프로세스 구현 가이드 | 토큰 관리 명세
로그인 및 인증 프로세스는 다음과 같은 단계로 진행됩니다:
- 로그인: 사용자 인증 정보로 로그인하여 액세스 토큰과 리프레시 토큰을 발급받습니다.
- 토큰 관리: 액세스 토큰 만료 시 리프레시 토큰을 사용하여 새로운 토큰을 발급받습니다.
- 로그아웃: 토큰을 무효화합니다.
중요: 로그인 API 호출 시에도 앱 보안 인증을 통해 발급받은
appToken을Authorization헤더에 포함해야 합니다. 로그인 성공 후에는 사용자 인증 토큰(accessToken)이 발급되며, 이후 사용자 인증이 필요한 모든 API 호출은 이accessToken을 사용합니다. 토큰 갱신 및 검증 API 호출 시에도appToken을 사용해야 합니다.
5.2.1 로그인 API
- HTTP 메서드: POST
- 경로: /auth/login
- Headers:
- Content-Type: application/json
- Authorization: Bearer
{appToken}
중요: PIN 코드는 ISO27001, GDPR, DiGA 규제 준수를 위해 서버로 전송되지 않습니다. 모바일 앱에서는 사용자가 입력한 PIN 코드를 통해 로컬에 안전하게 저장된 인증 정보(ID/PW)를 복호화한 후, 이 인증 정보를 사용하여 로그인을 수행합니다. 이 방식은 사용자의 PIN 코드가 네트워크를 통해 전송되거나 서버에 저장되는 것을 방지하여 보안을 강화합니다.
참고: 모바일 앱 재설치 시에는 저장된 PIN 코드와 인증 정보가 손실됩니다. 이 경우 사용자는 이메일/비밀번호를 사용한 전체 로그인 과정을 다시 수행하고, 로그인 성공 후 새로운 PIN 코드를 설정해야 합니다. 상세한 재설치 시나리오 처리 방법은 인증 기술 명세서를 참조하세요.
보안 요구사항: 사용자 비밀번호는 반드시 해시된 형태로 서버에 전송되어야 합니다. 로그인 요청 시 일반 텍스트 비밀번호 대신
passwordHash필드를 사용하세요. 해시는 SHA-256 알고리즘과 솔트(appSecret + deviceUUID)를 함께 사용합니다.
요청 (Request)
{
"email": "user@example.com",
"passwordHash": "base64EncodedPasswordHashString",
"deviceId": "device_123"
}
응답 (Response)
- 성공 응답 (200 OK)
{
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "eyJhbGciOiJIUzI1NiIs...",
"expiresIn": 3600,
"tokenType": "Bearer",
"user": {
"userId": "user_123",
"email": "user@example.com",
"name": "John Doe"
}
}
5.2.2 토큰 갱신
- HTTP 메서드: POST
- 경로: /auth/token/refresh
- Headers:
- Content-Type: application/json
- Authorization: Bearer
{appToken}
참고: 디바이스 정보는 refreshToken JWT에서 자동으로 추출되므로 별도의 deviceId 파라미터가 필요하지 않습니다.
요청 (Request)
{
"refreshToken": "eyJhbGciOiJIUzI1NiIs..."
}
응답 (Response)
- 성공 응답 (200 OK)
{
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "eyJhbGciOiJIUzI1NiIs...",
"expiresIn": 3600,
"tokenType": "Bearer"
}
5.2.3 토큰 검증
- HTTP 메서드: POST
- 경로: /auth/token/validate
- Headers:
- Content-Type: application/json
- Authorization: Bearer
{appToken}
요청 (Request)
{
"token": "eyJhbGciOiJIUzI1NiIs..."
}
응답 (Response)
- 성공 응답 (200 OK)
{
"valid": true,
"expiresIn": 1800
}
5.2.4 로그아웃
- HTTP 메서드: POST
- 경로: /auth/logout
- Headers:
- Authorization: Bearer
{accessToken}
- Authorization: Bearer
참고: 로그아웃 시 디바이스 정보는 JWT accessToken에서 자동으로 추출되므로 별도의 요청 파라미터가 필요하지 않습니다. 토큰 무효화는 JWT의
jti(토큰 ID)를 블랙리스트에 추가하는 방식으로 처리됩니다.
응답 (Response)
- 성공 응답 (200 OK)
{
"message": "Successfully logged out"
}
5.3 동의 관리 프로세스
📌 참고: 동의 관리 프로세스에 대한 자세한 내용은 동의 도메인 엔드포인트 문서를 참조하세요.
경로 변경 안내: 동의 관리 API는 기존
/auth/consents에서/consents로 경로가 변경되었습니다.
등록 프로세스 중단
📌 구현 순서: 회원가입 프로세스가 구현된 후에 진행합니다.
등록 프로세스 중단 API는 진행 중인 사용자 등록 과정에서 발생하는 중단 상황을 처리하고, 관련 캐시 데이터들을 정리하여 재가입을 가능하게 합니다.
5.4.1 등록 프로세스 중단 API
- HTTP 메서드: DELETE
- 경로: /v1/auth/registration/abort
- Headers:
- Content-Type: application/json
- Authorization: Bearer
{appToken}(필수)
- 비고: 진행 중인 사용자 등록 프로세스를 중단하고 관련 캐시 데이터들(이메일 인증 토큰, access-code 인증 상태, 등록 세션 등)을 정리합니다. 중단된 등록 프로세스로 인해 재가입이 불가능한 상황을 해결합니다. 이메일/accessCode 정보가 없는 경우 appToken의 deviceId를 사용하여 해당 디바이스와 연관된 모든 등록 캐시를 정리합니다.
요청 (Request) - Device ID 기반 등록 프로세스 중단 (기본 방식)
{
"reason": "앱 크래시 후 등록 프로세스 재시작"
}
참고: 요청 본문이 비어있거나 email/accessCode가 제공되지 않은 경우, appToken에서 추출한 deviceId를 사용하여 해당 디바이스와 연관된 모든 등록 관련 캐시를 정리합니다. 이는 앱 크래시, 강제 종료 등으로 클라이언트에서 이메일이나 accessCode 정보를 잃어버린 경우에 유용합니다.
요청 (Request) - 특정 이메일/Access Code 기반 (선택적)
{
"email": "user@example.com", // 또는 "accessCode": "ABCD1234EFGH5678"
"reason": "특정 이메일의 등록 프로세스만 중단"
}
참고: 특정 이메일이나 Access Code의 등록 프로세스만 중단하고 싶은 경우에만 사용합니다. 일반적으로는 위의 deviceId 기반 방식을 사용하는 것이 더 편리합니다.
응답 (Response)
- 성공 응답 (200 OK) - deviceId 기반 (기본 방식)
{
"message": "디바이스의 모든 등록 프로세스가 성공적으로 중단되었습니다.",
"deviceId": "device_123",
"clearedCaches": ["EMAIL_VERIFICATION_TOKEN", "ACCESS_CODE_VERIFICATION", "REGISTRATION_SESSION", "DEVICE_REGISTRATION_STATE"],
"abortedAt": 1714538000000
}
- 성공 응답 (200 OK) - 특정 이메일/accessCode 기반 (선택적)
{
"message": "등록 프로세스가 성공적으로 중단되었습니다.",
"email": "user@example.com", // 또는 "accessCode": "ABCD1234EFGH5678"
"clearedCaches": ["EMAIL_VERIFICATION_TOKEN", "ACCESS_CODE_VERIFICATION", "REGISTRATION_SESSION"],
"abortedAt": 1714538000000
}
- 오류 응답 (404 Not Found) - 진행 중인 등록 프로세스가 없는 경우
{
"code": 2071,
"message": "REGISTRATION_PROCESS_NOT_FOUND",
"detail": "해당 이메일, Access Code 또는 디바이스로 진행 중인 등록 프로세스를 찾을 수 없습니다."
}
- 오류 응답 (409 Conflict) - 이미 완료된 등록인 경우
{
"code": 2072,
"message": "REGISTRATION_ALREADY_COMPLETED",
"detail": "해당 이메일로 이미 등록이 완료된 사용자가 존재합니다."
}
- 오류 응답 (500 Internal Server Error) - 캐시 정리 실패
{
"code": 2073,
"message": "REGISTRATION_ABORT_FAILED",
"detail": "등록 프로세스 중단 중 오류가 발생했습니다."
}
계정 비활성화 (탈퇴)
📌 API 레퍼런스: 계정 비활성화 API
계정 비활성화 API는 현재 로그인한 사용자의 계정을 비활성화(Soft Delete)합니다. 일반 사용자와 게스트 사용자 모두 이 API를 통해 자발적으로 탈퇴할 수 있습니다.
5.5.1 계정 비활성화 API
- HTTP 메서드: DELETE
- 경로: /v1/auth/me
- Headers:
- Content-Type: application/json (선택)
- Authorization: Bearer
{accessToken}(필수)
- 비고: 현재 로그인한 사용자의 계정을 비활성화합니다. 게스트 계정의 경우 만료 타이머도 함께 해제됩니다.
요청 (Request)
{
"reason": "더 이상 서비스를 이용하지 않습니다."
}
참고: 요청 본문은 선택 사항입니다. 탈퇴 사유를 기록하고 싶은 경우에만 전송합니다.
응답 (Response)
- 성공 응답 (200 OK)
{
"message": "계정이 성공적으로 비활성화되었습니다.",
"userId": "123e4567-e89b-12d3-a456-426614174000",
"status": "INACTIVE",
"deletedAt": 1702819200000
}
- 오류 응답 (400 Bad Request) - 이미 비활성화된 계정
{
"message": "이미 비활성화된 사용자입니다."
}
- 오류 응답 (401 Unauthorized) - 인증 실패
{
"code": 2051,
"message": "INVALID_TOKEN",
"detail": "유효한 사용자 인증 정보가 없습니다."
}
- 오류 응답 (404 Not Found) - 사용자를 찾을 수 없음
{
"message": "사용자를 찾을 수 없습니다."
}
GesundheitsID 인증 및 연동
📌 기술 구현 문서: GesundheitsID 연동 가이드 | GesundheitsID 명세
📌 구현 순서: 인증 핵심 기능 API가 구현된 후에 진행합니다.
GesundheitsID는 독일 의료 시스템에서 사용되는 디지털 건강 ID로, 사용자는 기존 DTA 계정과 GesundheitsID를 연동하여 추가적인 인증 방식으로 사용할 수 있습니다.
6.1 GesundheitsID 연동 초기화
- HTTP 메서드: POST
- 경로: /auth/gesundheitsid/link/initialize
- Headers:
- Content-Type: application/json
- Authorization: Bearer
{accessToken}
요청 (Request)
{
"redirectUri": "dta://gesundheitsid-callback"
}
응답 (Response)
- 성공 응답 (200 OK)
{
"authUrl": "https://gesundheitsid.de/authorize?client_id=abc&state=xyz&redirect_uri=dta://gesundheitsid-callback",
"state": "xyz123",
"expiresIn": 600
}
- 오류 응답 (401 Unauthorized)
{
"code": 2001,
"message": "UNAUTHORIZED",
"detail": "인증이 필요합니다."
}
6.2 GesundheitsID 연동 완료
- HTTP 메서드: POST
- 경로: /auth/gesundheitsid/link/complete
- Headers:
- Content-Type: application/json
- Authorization: Bearer
{accessToken}
요청 (Request)
{
"code": "auth_code_from_gesundheitsid",
"state": "xyz123"
}
응답 (Response)
- 성공 응답 (200 OK)
{
"linked": true,
"gesundheitsId": "g-id-12345",
"linkedAt": 1711011900000
}
- 오류 응답 (400 Bad Request)
{
"code": 2050,
"message": "INVALID_GESUNDHEITSID_CODE",
"detail": "유효하지 않은 GesundheitsID 인증 코드입니다."
}
- 오류 응답 (400 Bad Request)
{
"code": 2051,
"message": "INVALID_STATE",
"detail": "state 값이 일치하지 않습니다."
}
6.3 GesundheitsID 연동 상태 조회
- HTTP 메서드: GET
- 경로: /auth/gesundheitsid/link
- Headers:
- Authorization: Bearer
{accessToken}
- Authorization: Bearer
응답 (Response)
- 성공 응답 (200 OK)
{
"linked": true,
"gesundheitsId": "g-id-12345",
"linkedAt": 1711011900000,
"lastUsedAt": 1711016700000
}
- 성공 응답 (연동되지 않은 경우) (200 OK)
{
"linked": false
}
6.4 GesundheitsID 연동 해제
- HTTP 메서드: POST
- 경로: /auth/gesundheitsid/unlink
- Headers:
- Authorization: Bearer
{accessToken}
- Authorization: Bearer
응답 (Response)
- 성공 응답 (200 OK)
{
"unlinked": true,
"unlinkedAt": 1711019400000
}
- 오류 응답 (400 Bad Request)
{
"code": 2052,
"message": "GESUNDHEITSID_NOT_LINKED",
"detail": "GesundheitsID가 연동되어 있지 않습니다."
}
6.5 GesundheitsID 로그인 초기화
- HTTP 메서드: POST
- 경로: /auth/gesundheitsid/login/initialize
- Headers:
- Content-Type: application/json
- Authorization: Bearer
{appToken}
요청 (Request)
{
"redirectUri": "dta://gesundheitsid-login-callback",
"deviceId": "device_123"
}
응답 (Response)
- 성공 응답 (200 OK)
{
"authUrl": "https://gesundheitsid.de/authorize?client_id=abc&state=xyz&redirect_uri=dta://gesundheitsid-login-callback",
"state": "xyz123",
"expiresIn": 600
}
6.6 GesundheitsID 로그인 완료
- HTTP 메서드: POST
- 경로: /auth/gesundheitsid/login/complete
- Headers:
- Content-Type: application/json
- Authorization: Bearer
{appToken}
요청 (Request)
{
"code": "auth_code_from_gesundheitsid",
"state": "xyz123",
"deviceId": "device_123"
}
응답 (Response)
- 성공 응답 (200 OK)
{
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "eyJhbGciOiJIUzI1NiIs...",
"expiresIn": 3600,
"tokenType": "Bearer",
"user": {
"userId": "user_123",
"email": "user@example.com",
"name": "홍길동"
}
}
- 오류 응답 (400 Bad Request)
{
"code": 2053,
"message": "GESUNDHEITSID_LOGIN_FAILED",
"detail": "GesundheitsID 로그인에 실패했습니다."
}
- 오류 응답 (404 Not Found)
{
"code": 2054,
"message": "GESUNDHEITSID_NOT_FOUND",
"detail": "연동된 GesundheitsID를 찾을 수 없습니다."
}
내부 운영자 인증 API
📌 기술 구현 문서: TBD
📌 구현 순서: 인증 핵심 기능 API가 구현된 후에 진행합니다.
내부 운영자 인증 API는 서비스 관리자, 의료진, 기술 담당자 등 내부 운영자를 위한 인증 서비스를 제공합니다. 이 API는 고객(환자) 인증과 유사하지만, 데이터는 operation 스키마에 저장되며 접근 권한이 더 제한적입니다.
7.1 내부 운영자 등록 API
- HTTP 메서드: POST
- 경로: /auth/operation/register
- Headers:
- Content-Type: application/json
- Authorization: Bearer
{systemAdminToken}
중요: 내부 운영자는 System Admin 권한을 가진 사용자만 등록할 수 있습니다.
요청 (Request)
{
"email": "admin@example.com",
"passwordHash": "base64EncodedPasswordHashString",
"verificationId": "ver_456",
"userType": "OPERATION_USER",
"profile": {
"firstName": "관리자",
"lastName": "김",
"language": "ko",
"timezone": "Asia/Seoul"
},
"roles": ["SYSTEM_ADMIN"]
}
참고:
userType은 "OPERATION_USER" 또는 "SERVICE_ACCOUNT" 중 하나여야 합니다.roles는 IAM 도메인에서 정의된 역할 ID 목록입니다.
응답 (Response)
- 성공 응답 (201 Created)
{
"userId": "op_user_123",
"email": "admin@example.com",
"userType": "OPERATION_USER",
"profile": {
"firstName": "관리자",
"lastName": "김",
"language": "ko",
"timezone": "Asia/Seoul"
},
"roles": ["SYSTEM_ADMIN"],
"createdAt": 1711011600000
}
7.2 내부 운영자 삭제 API
- HTTP 메서드: DELETE
- 경로: /auth/operation/user/
{id} - Headers:
- Authorization: Bearer
{systemAdminToken}
- Authorization: Bearer
중요: 내부 운영자는 System Admin 권한을 가진 사용자만 삭제할 수 있습니다.
응답 (Response)
- 성공 응답 (200 OK)
{
"message": "Operation user successfully deleted",
"userId": "op_user_123",
"deletedAt": 1711019400000
}
7.3 내부 운영자 이메일 인증 코드 발송 API
- HTTP 메서드: POST
- 경로: /auth/operation/email/verification-code
- Headers:
- Content-Type: application/json
- Authorization: Bearer
{systemAdminToken 또는 appToken}
요청 (Request)
{
"email": "admin@example.com"
}
응답 (Response)
- 성공 응답 (200 OK)
{
"email": "admin@example.com",
"expiresIn": 300,
"requestId": "req_op_123"
}
7.4 내부 운영자 이메일 인증 코드 확인 API
- HTTP 메서드: POST
- 경로: /auth/operation/email/verify
- Headers:
- Content-Type: application/json
- Authorization: Bearer
{systemAdminToken 또는 appToken}
요청 (Request)
{
"email": "admin@example.com",
"code": "123456",
"requestId": "req_op_123"
}
응답 (Response)
- 성공 응답 (200 OK)
{
"verified": true,
"verificationId": "ver_op_456",
"verifiedAt": 1711011900000,
"expiresAt": 1711013700000
}
7.5 내부 운영자 로그인 API
- HTTP 메서드: POST
- 경로: /auth/operation/login
- Headers:
- Content-Type: application/json
- Authorization: Bearer
{appToken}
요청 (Request)
{
"email": "admin@example.com",
"passwordHash": "base64EncodedPasswordHashString",
"deviceId": "device_123"
}
응답 (Response)
- 성공 응답 (200 OK)
{
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "eyJhbGciOiJIUzI1NiIs...",
"expiresIn": 3600,
"tokenType": "Bearer",
"user": {
"userId": "op_user_123",
"email": "admin@example.com",
"userType": "OPERATION_USER",
"roles": ["SYSTEM_ADMIN"]
}
}
7.6 내부 운영자 로그아웃 API
- HTTP 메서드: POST
- 경로: /auth/operation/logout
- Headers:
- Authorization: Bearer
{operationUserToken}
- Authorization: Bearer
참고: 내부 운영자 로그아웃 시에도 디바이스 정보는 JWT 토큰에서 자동으로 추출되므로 별도의 요청 파라미터가 필요하지 않습니다.
응답 (Response)
- 성공 응답 (200 OK)
{
"message": "Successfully logged out"
}
7.7 내부 운영자 토큰 갱신 API
- HTTP 메서드: POST
- 경로: /auth/operation/token/refresh
- Headers:
- Content-Type: application/json
- Authorization: Bearer
{operationUserToken}
참고: 내부 운영자 토큰 갱신 시에도 디바이스 정보는 refreshToken JWT에서 자동으로 추출되므로 별도의 deviceId 파라미터가 필요하지 않습니다.
요청 (Request)
{
"refreshToken": "eyJhbGciOiJIUzI1NiIs..."
}
응답 (Response)
- 성공 응답 (200 OK)
{
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "eyJhbGciOiJIUzI1NiIs...",
"expiresIn": 3600,
"tokenType": "Bearer"
}
7.8 내부 운영자 토큰 검증 API
- HTTP 메서드: POST
- 경로: /auth/operation/token/validate
- Headers:
- Content-Type: application/json
- Authorization: Bearer
{appToken}
요청 (Request)
{
"token": "eyJhbGciOiJIUzI1NiIs..."
}
응답 (Response)
- 성공 응답 (200 OK)
{
"valid": true,
"expiresIn": 1800,
"userType": "ADMIN",
"roles": ["SYSTEM_ADMIN"]
}
오류 코드
Auth 도메인 API에서 사용하는 오류 코드는 다음과 같습니다:
| HTTP 상태 코드 | 오류 코드 | 메시지 | 설명 | 대응 방법 |
|---|---|---|---|---|
| 500 | 2000 | SERVER_ERROR | 서버 내부 오류 | 서버 로그를 확인하고 시스템 관리자에게 문의하세요. |
| 401 | 2001 | UNAUTHORIZED | 인증이 필요합니다 | 유효한 토큰으로 재인증하세요. |
| 403 | 2002 | FORBIDDEN | 권한이 없습니다 | 필요한 권한을 확인하고 적절한 권한을 가진 계정으로 요청하세요. |
| 401 | 2003 | INVALID_APP_TOKEN | 유효하지 않은 토큰입니다 | 올바른 토큰으로 다시 요청하거나 재로그인하세요. |
| 401 | 2004 | APP_TOKEN_EXPIRED | 토큰이 만료되었습니다 | 리프레시 토큰을 사용하여 새로운 토큰을 발급받으세요. |
| 401 | 2005 | INVALID_REFRESH_TOKEN | 유효하지 않은 리프레시 토큰입니다 | 재로그인하여 새 리프레시 토큰을 발급받으세요. |
| 401 | 2006 | REFRESH_TOKEN_EXPIRED | 리프레시 토큰이 만료되었습니다 | 재로그인하여 새 토큰을 발급받으세요. |
| 401 | 2007 | INVALID_CREDENTIALS | 이메일 또는 비밀번호가 올바르지 않습니다 | 올바른 이메일과 비밀번호를 입력하세요. |
| 400 | 2007_1 | PASSWORD_HASH_INVALID | 비밀번호 해시가 유효하지 않습니다 | 올바른 해시 알고리즘과 솔트를 사용했는지 확인하세요. |
| 400 | 2007_2 | MISSING_PASSWORD_HASH | 해시된 비밀번호가 필요합니다 | 비밀번호를 해시하여 전송하세요. |
| 423 | 2008 | ACCOUNT_LOCKED | 계정이 잠겼습니다 | 고객센터에 문의하거나 비밀번호 재설정을 시도하세요. |
| 400 | 2009 | INVALID_DEVICE_ID | 디바이스 ID가 유효하지 않습니다 | 올바른 디바이스 정보로 다시 요청하세요. |
| 401 | 2010 | CHALLENGE_VERIFICATION_FAILED | 챌린지 검증에 실패했습니다 | 새로운 챌린지를 요청하고 앱 무결성을 확인하세요. |
| 403 | 2011 | DEVICE_BLACKLISTED | 디바이스가 블랙리스트에 등록되어 있습니다 | 계정 보안을 위해 고객센터에 문의하세요. |
| 400 | 2012 | INVALID_MFA_CODE | 유효하지 않은 2단계 인증 코드입니다 | 올바른 2단계 인증 코드를 입력하거나 새 코드를 요청하세요. |
| 400 | 2013 | EMAIL_NOT_VERIFIED | 이메일 인증이 완료되지 않았습니다 | 이메일 인증 과정을 완료하세요. |
| 400 | 2014 | VERIFICATION_CODE_EXPIRED | 인증 코드가 만료되었습니다 | 새 인증 코드를 요청하세요. |
| 401 | 2015 | INVALID_APP_TOKEN | 앱 토큰이 유효하지 않거나 만료되었습니다 | 앱 인증 과정을 다시 수행하세요. |
| 400 | 2026 | EMAIL_VERIFICATION_ALREADY_COMPLETED | 이미 인증이 완료된 이메일입니다 | 이미 인증된 이메일이므로 추가 인증이 필요하지 않습니다. |
| 400 | 2028 | REGISTRATION_VALIDATION_FAILED | 가입 정보 검증에 실패했습니다 | 입력한 정보가 유효한지 확인하고 필요한 정보를 모두 제공했는지 검토하세요. |
| 429 | 2040 | RATE_LIMIT_EXCEEDED | 요청 횟수가 제한을 초과했습니다 | 잠시 후에 다시 시도하세요. |
| 400 | 2050 | INVALID_GESUNDHEITSID_CODE | 유효하지 않은 GesundheitsID 인증 코드입니다 | GesundheitsID 인증 과정을 다시 시작하세요. |
| 400 | 2051 | INVALID_STATE | state 값이 일치하지 않습니다 | 보안을 위해 인증 과정을 처음부터 다시 시작하세요. |
| 400 | 2052 | GESUNDHEITSID_NOT_LINKED | GesundheitsID가 연동되어 있지 않습니다 | GesundheitsID 연동 과정을 먼저 완료하세요. |
| 400 | 2053 | GESUNDHEITSID_LOGIN_FAILED | GesundheitsID 로그인에 실패했습니다 | GesundheitsID 계정 정보를 확인하고 다시 시도하세요. |
| 404 | 2054 | GESUNDHEITSID_NOT_FOUND | 연동된 GesundheitsID를 찾을 수 없습니다 | GesundheitsID 연동 상태를 확인하고 필요시 재연동하세요. |
| 403 | 2060 | OPERATION_ACCESS_DENIED | 내부 운영자 접근 권한이 없습니다 | 내부 운영자 권한이 필요한 API입니다. |
| 400 | 2061 | INVALID_USER_TYPE | 유효하지 않은 사용자 유형입니다 | 유효한 운영자 유형을 입력하세요 (ADMIN, OPERATOR, CLINICIAN, SERVICE_ACCOUNT). |
| 400 | 2062 | INVALID_ROLE | 유효하지 않은 역할입니다 | 유효한 역할 ID를 입력하세요. |
| 404 | 2063 | OPERATION_USER_NOT_FOUND | 내부 운영자를 찾을 수 없습니다 | 내부 운영자 ID를 확인하세요. |
| 404 | 2071 | REGISTRATION_PROCESS_NOT_FOUND | 진행 중인 등록 프로세스를 찾을 수 없습니다 | 해당 이메일, Access Code 또는 디바이스로 진행 중인 등록 프로세스가 없습니다. |
| 409 | 2072 | REGISTRATION_ALREADY_COMPLETED | 이미 등록이 완료된 사용자가 존재합니다 | 해당 이메일로 이미 가입이 완료되어 등록 프로세스를 중단할 수 없습니다. |
| 500 | 2073 | REGISTRATION_ABORT_FAILED | 등록 프로세스 중단 처리 실패 | 등록 프로세스 중단 중 오류가 발생했습니다. 다시 시도하거나 고객센터에 문의하세요. |
| 401 | 2074 | IDENTITY_LEVEL_ALREADY_REGISTERED | 이미 정규 사용자로 승격된 계정입니다 | 일반 로그인 플로우를 이용하거나 기존 자격 증명을 확인하세요. |
| 401 | 2075 | DEVICE_MISMATCH_FOR_STEP_UP | Step-up 인증은 동일 디바이스에서만 수행할 수 있습니다 | 게스트 토큰을 발급받은 디바이스에서 다시 시도하세요. |
| 404 | 2076 | LINK_SESSION_NOT_FOUND | 유효한 Step-up 세션을 찾을 수 없습니다 | 세션이 만료되었거나 취소되었습니다. 새 Step-up 세션을 생성하세요. |
| 429 | 2077 | STEP_UP_TOO_MANY_ATTEMPTS | Step-up 인증 재시도 횟수를 초과했습니다 | 잠시 후 다시 시도하거나 고객센터에 문의하세요. |
| 400 | 3001 | INVALID_ACCESS_CODE | 유효하지 않은 Access Code입니다 | 올바른 Access Code를 입력하세요. |
| 400 | 3002 | ACCESS_CODE_ALREADY_USED | 이미 사용된 Access Code입니다 | 새로운 Access Code를 발급받아 사용하세요. |
| 400 | 3003 | ACCESS_CODE_EXPIRED | 만료된 Access Code입니다 | 새로운 Access Code를 발급받아 사용하세요. |
| 400 | 3010 | ACCESS_CODE_NOT_VALIDATED | Access Code 검증이 완료되지 않았습니다 | 회원가입 전에 Access Code 검증 과정을 완료하세요. |
| 404 | 1100 | TERMS_NOT_FOUND | 약관을 찾을 수 없습니다 | 약관 ID를 확인하고 다시 시도하세요. |
| 400 | 1101 | INVALID_TERMS_STATUS | 유효하지 않은 약관 상태입니다 | 약관 상태 값을 확인하고 올바른 값을 입력하세요. |
| 404 | 1102 | TERMS_TRANSLATION_NOT_FOUND | 약관 번역을 찾을 수 없습니다 | 언어 코드와 약관 ID를 확인하세요. |
| 400 | 1103 | REQUIRED_TERMS_NOT_AGREED | 필수 약관에 동의하지 않았습니다 | 필수 약관에 동의 후 다시 시도하세요. |
| 409 | 1104 | DUPLICATE_TERMS_TRANSLATION | 이미 존재하는 약관 번역입니다 | 다른 언어로 번역을 추가하거나 기존 번역을 업데이트하세요. |
| 400 | 1105 | ACTIVE_TERMS_ALREADY_EXISTS | 동일한 유형의 활성화된 약관이 이미 존재합니다 | 기존 활성 약관을 비활성화한 후 다시 시도하세요. |
| 404 | 1106 | TERMS_AGREEMENT_NOT_FOUND | 약관 동의 정보를 찾을 수 없습니다 | 사용자 ID와 약관 ID를 확인하세요. |
| 409 | 2020 | EMAIL_ALREADY_EXISTS | 이미 사용 중인 이메일입니다 | 다른 이메일 주소를 사용하거나 로그인을 시도하세요. |
| 404 | 2021 | EMAIL_NOT_FOUND | 존재하지 않는 이메일입니다 | 이메일 주소를 확인하거나 회원가입을 진행하세요. |
| 422 | 2027 | UNREGISTERED_EMAIL | 가입되지 않은 이메일입니다 | 이메일 주소를 확인하거나 회원가입을 진행하세요. |
변경 이력
| 버전 | 날짜 | 작성자 | 변경 내용 |
|---|---|---|---|
| 0.1.0 | 2025-03-01 | bok@weltcorp.com | 최초 작성 |
| 0.2.0 | 2025-03-25 | bok@weltcorp.com | 초기 버전 작성 |
| 0.3.0 | 2025-04-11 | bok@weltcorp.com | 앱 인증 API 엔드포인트 이름 변경 (verify → complete-challenge, validate → verify-token) |
| 0.4.0 | 2025-04-11 | bok@weltcorp.com | 앱 토큰 검증 API 제거, 각 API에서 토큰 검증하도록 간소화 |
| 0.5.0 | 2025-04-11 | bok@weltcorp.com | 앱 토큰 조회 API 제거, 섹션 번호 조정 |
| 0.6.0 | 2025-04-30 | bok@weltcorp.com | 회원가입용과 비밀번호 재설정용 이메일 인증 코드 발송 API 분리 |
| 0.7.0 | 2025-05-07 | bok@weltcorp.com | 내부 운영자(operation user) 인증 관련 API 추가 |
| 0.8.0 | 2025-05-07 | bok@weltcorp.com | UNREGISTERED_EMAIL 오류 코드 추가 (422 HTTP 상태 코드) |
| 0.9.0 | 2025-05-08 | bok@weltcorp.com | 이메일 인증 API에 타입 필드 추가 (REGISTRATION, LOGIN, PASSWORD_RESET), /auth/terms-and-consents 경로를 /auth/agreements로 변경 |
| 0.9.1 | 2025-05-08 | bok@weltcorp.com | 별도 비밀번호 재설정용 이메일 인증 API 제거, 통합 이메일 인증 API로 일원화 |
| 0.9.2 | 2025-05-15 | bok@weltcorp.com | REGISTRATION_VALIDATION_FAILED (2028) 오류 코드 추가 |
| 0.10.0 | 2025-07-28 | bok@weltcorp.com | 등록 프로세스 중단 API 추가 (DELETE /v1/auth/registration/abort), deviceId 기반 캐시 정리 지원, 관련 오류 코드 추가 (2071-2073) |
| 0.11.0 | 2025-08-11 | bok@weltcorp.com | JWT stateless 인증 방식에 맞게 API 간소화: 로그아웃, 토큰 갱신 API에서 deviceId 파라미터 제거 (JWT에서 자동 추출) |
| 0.12.0 | 2025-10-27 | bok@weltcorp.com | 게스트 온보딩/Step-up API 추가 및 관련 오류 코드 확장 |