본문으로 건너뛰기

Auth API 엔드포인트

목차

접근 권한 매트릭스

엔드포인트System AdminIAM AdminService AccountRegular User비고
POST /v1/auth/app/challenge
POST /v1/auth/app/complete-challenge
POST /v1/auth/access-code/validate
GET /v1/auth/agreementsApp/Access Token
POST /v1/auth/email/verification-code
POST /v1/auth/email/verify
POST /v1/auth/register
POST /v1/auth/guest/registerApp Token, identityLevel=guest 발급
POST /v1/auth/guest/link/init게스트 토큰 + 동일 디바이스 필요
POST /v1/auth/guest/link/completeStep-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/anonymousappToken 필수(익명 토큰 발급), 기본 클레임 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 타임스탬프(밀리초) 형식을 사용합니다. 시간 데이터 처리에 대한 자세한 내용은 데이터 타입 가이드라인을 참조하세요.

앱 인증 토큰 사용 흐름

  1. 앱 보안 인증

    • 디바이스 정보를 이용해 챌린지 요청(/auth/app/challenge) 및 응답(/auth/app/complete-challenge)을 통해 appToken 발급
    • 모든 API 요청에 Authorization: Bearer {appToken} 헤더 포함
  2. 사용자 미인증 상태에서의 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"
}

참고: deviceIdHashdeviceId 객체를 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}
응답 예시
{
"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}
응답 예시
{
"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
요청 (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-KRregion: "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
}
}
}
필드타입필수설명
agreementsarrayYes약관 동의 목록 (정식 회원가입과 동일한 형식)
deviceUuidstringYes디바이스 UUID (앱 인증 API의 deviceId.uuid와 동일한 값 사용)
profileobjectYes사용자 프로필 정보 (정식 회원가입과 동일한 형식)
profile.languagestringYes사용자 언어 설정 (예: ko-KR, de-DE)
profile.timezone.idstringYes시간대 ID (예: Asia/Seoul, Europe/Berlin)
profile.timezone.offsetInMinutesnumberYesUTC 기준 오프셋 (분 단위)

응답 본문 (201 Created)

{
"success": true
}
필드타입필수설명
successbooleanYes게스트 등록 성공 여부

ℹ️ 참고: 게스트 등록 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"
}
필드타입필수설명
deviceUuidstringYes디바이스 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": [...]
}
필드타입필수설명
tokensarrayYes인증 토큰 배열 (access + refresh)
userobjectYes사용자(게스트) 계정 정보
user.idstringYes사용자 계정 ID
user.identityLevelstringYes신원 수준 (게스트의 경우 GUEST)
user.createdAtnumberYes계정 생성일 (Unix timestamp ms)
user.expiresAtnumberNo세션 만료일 (Unix timestamp ms)
userCycleobjectYes사용자 주기 정보 (정식 사용자 login API의 userCycle과 동일)
userCycle.idstringYes사용자 주기 ID
userCycle.statusstringYes주기 상태 (ACTIVE)
userCycle.startedAtnumberYes주기 시작일 (Unix timestamp ms)
userCycle.countnumberYes총 주기 개수 (게스트는 항상 1)
userCycle.treatmentDurationDaysnumberYes치료 기간 (일수). 게스트는 -1 (무제한)
profileobjectYes프로필 정보
rolesstring[]Yes역할 목록
permissionsstring[]Yes권한 목록
agreementsarrayYes동의한 약관 목록

⚠️ 오류 응답

  • 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 회원가입 프로세스

📌 기술 구현 문서: 회원가입 프로세스 구현 가이드 | 인증 서비스 명세

회원가입은 다음과 같은 단계로 진행됩니다:

  1. Access Code 검증: 사용자가 제출한 16자리 Access Code의 유효성을 검증합니다.
  2. 통합 동의 항목 조회: 회원가입 화면에 표시해야 할 필수 약관 및 선택적 동의 항목 목록과 상태를 조회합니다.
  3. 이메일 인증 코드 발송: 사용자가 입력한 이메일로 6자리 OTP 번호를 발송합니다.
  4. 이메일 인증 코드 확인: 사용자가 수신한 OTP 번호를 입력하여 이메일 유효성을 검증합니다.
  5. 회원가입: Access Code 검증, 약관 동의 정보와 인증이 완료된 이메일을 사용하여 회원가입을 진행합니다. 이때 클라이언트는 동의한 약관 정보를 함께 전달합니다.

중요: 회원가입 프로세스의 모든 단계는 사용자 로그인 전이므로, 각 API 호출 시 앱 보안 인증을 통해 발급받은 appTokenAuthorization: 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"
}
필드타입설명필수
emailstring인증 코드를 받을 이메일 주소Yes
typestring이메일 인증 타입 (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"
}
필드타입설명필수
emailstring인증 코드를 받은 이메일 주소Yes
codestring이메일로 받은 6자리 인증 코드Yes
requestIdstring코드 발송 요청 시 받은 요청 IDYes
typestring이메일 인증 타입 (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 로그인 및 인증 프로세스

📌 기술 구현 문서: 로그인 프로세스 구현 가이드 | 토큰 관리 명세

로그인 및 인증 프로세스는 다음과 같은 단계로 진행됩니다:

  1. 로그인: 사용자 인증 정보로 로그인하여 액세스 토큰과 리프레시 토큰을 발급받습니다.
  2. 토큰 관리: 액세스 토큰 만료 시 리프레시 토큰을 사용하여 새로운 토큰을 발급받습니다.
  3. 로그아웃: 토큰을 무효화합니다.

중요: 로그인 API 호출 시에도 앱 보안 인증을 통해 발급받은 appTokenAuthorization 헤더에 포함해야 합니다. 로그인 성공 후에는 사용자 인증 토큰(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}

참고: 로그아웃 시 디바이스 정보는 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}
응답 (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}
응답 (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}

중요: 내부 운영자는 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}

참고: 내부 운영자 로그아웃 시에도 디바이스 정보는 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 상태 코드오류 코드메시지설명대응 방법
5002000SERVER_ERROR서버 내부 오류서버 로그를 확인하고 시스템 관리자에게 문의하세요.
4012001UNAUTHORIZED인증이 필요합니다유효한 토큰으로 재인증하세요.
4032002FORBIDDEN권한이 없습니다필요한 권한을 확인하고 적절한 권한을 가진 계정으로 요청하세요.
4012003INVALID_APP_TOKEN유효하지 않은 토큰입니다올바른 토큰으로 다시 요청하거나 재로그인하세요.
4012004APP_TOKEN_EXPIRED토큰이 만료되었습니다리프레시 토큰을 사용하여 새로운 토큰을 발급받으세요.
4012005INVALID_REFRESH_TOKEN유효하지 않은 리프레시 토큰입니다재로그인하여 새 리프레시 토큰을 발급받으세요.
4012006REFRESH_TOKEN_EXPIRED리프레시 토큰이 만료되었습니다재로그인하여 새 토큰을 발급받으세요.
4012007INVALID_CREDENTIALS이메일 또는 비밀번호가 올바르지 않습니다올바른 이메일과 비밀번호를 입력하세요.
4002007_1PASSWORD_HASH_INVALID비밀번호 해시가 유효하지 않습니다올바른 해시 알고리즘과 솔트를 사용했는지 확인하세요.
4002007_2MISSING_PASSWORD_HASH해시된 비밀번호가 필요합니다비밀번호를 해시하여 전송하세요.
4232008ACCOUNT_LOCKED계정이 잠겼습니다고객센터에 문의하거나 비밀번호 재설정을 시도하세요.
4002009INVALID_DEVICE_ID디바이스 ID가 유효하지 않습니다올바른 디바이스 정보로 다시 요청하세요.
4012010CHALLENGE_VERIFICATION_FAILED챌린지 검증에 실패했습니다새로운 챌린지를 요청하고 앱 무결성을 확인하세요.
4032011DEVICE_BLACKLISTED디바이스가 블랙리스트에 등록되어 있습니다계정 보안을 위해 고객센터에 문의하세요.
4002012INVALID_MFA_CODE유효하지 않은 2단계 인증 코드입니다올바른 2단계 인증 코드를 입력하거나 새 코드를 요청하세요.
4002013EMAIL_NOT_VERIFIED이메일 인증이 완료되지 않았습니다이메일 인증 과정을 완료하세요.
4002014VERIFICATION_CODE_EXPIRED인증 코드가 만료되었습니다새 인증 코드를 요청하세요.
4012015INVALID_APP_TOKEN앱 토큰이 유효하지 않거나 만료되었습니다앱 인증 과정을 다시 수행하세요.
4002026EMAIL_VERIFICATION_ALREADY_COMPLETED이미 인증이 완료된 이메일입니다이미 인증된 이메일이므로 추가 인증이 필요하지 않습니다.
4002028REGISTRATION_VALIDATION_FAILED가입 정보 검증에 실패했습니다입력한 정보가 유효한지 확인하고 필요한 정보를 모두 제공했는지 검토하세요.
4292040RATE_LIMIT_EXCEEDED요청 횟수가 제한을 초과했습니다잠시 후에 다시 시도하세요.
4002050INVALID_GESUNDHEITSID_CODE유효하지 않은 GesundheitsID 인증 코드입니다GesundheitsID 인증 과정을 다시 시작하세요.
4002051INVALID_STATEstate 값이 일치하지 않습니다보안을 위해 인증 과정을 처음부터 다시 시작하세요.
4002052GESUNDHEITSID_NOT_LINKEDGesundheitsID가 연동되어 있지 않습니다GesundheitsID 연동 과정을 먼저 완료하세요.
4002053GESUNDHEITSID_LOGIN_FAILEDGesundheitsID 로그인에 실패했습니다GesundheitsID 계정 정보를 확인하고 다시 시도하세요.
4042054GESUNDHEITSID_NOT_FOUND연동된 GesundheitsID를 찾을 수 없습니다GesundheitsID 연동 상태를 확인하고 필요시 재연동하세요.
4032060OPERATION_ACCESS_DENIED내부 운영자 접근 권한이 없습니다내부 운영자 권한이 필요한 API입니다.
4002061INVALID_USER_TYPE유효하지 않은 사용자 유형입니다유효한 운영자 유형을 입력하세요 (ADMIN, OPERATOR, CLINICIAN, SERVICE_ACCOUNT).
4002062INVALID_ROLE유효하지 않은 역할입니다유효한 역할 ID를 입력하세요.
4042063OPERATION_USER_NOT_FOUND내부 운영자를 찾을 수 없습니다내부 운영자 ID를 확인하세요.
4042071REGISTRATION_PROCESS_NOT_FOUND진행 중인 등록 프로세스를 찾을 수 없습니다해당 이메일, Access Code 또는 디바이스로 진행 중인 등록 프로세스가 없습니다.
4092072REGISTRATION_ALREADY_COMPLETED이미 등록이 완료된 사용자가 존재합니다해당 이메일로 이미 가입이 완료되어 등록 프로세스를 중단할 수 없습니다.
5002073REGISTRATION_ABORT_FAILED등록 프로세스 중단 처리 실패등록 프로세스 중단 중 오류가 발생했습니다. 다시 시도하거나 고객센터에 문의하세요.
4012074IDENTITY_LEVEL_ALREADY_REGISTERED이미 정규 사용자로 승격된 계정입니다일반 로그인 플로우를 이용하거나 기존 자격 증명을 확인하세요.
4012075DEVICE_MISMATCH_FOR_STEP_UPStep-up 인증은 동일 디바이스에서만 수행할 수 있습니다게스트 토큰을 발급받은 디바이스에서 다시 시도하세요.
4042076LINK_SESSION_NOT_FOUND유효한 Step-up 세션을 찾을 수 없습니다세션이 만료되었거나 취소되었습니다. 새 Step-up 세션을 생성하세요.
4292077STEP_UP_TOO_MANY_ATTEMPTSStep-up 인증 재시도 횟수를 초과했습니다잠시 후 다시 시도하거나 고객센터에 문의하세요.
4003001INVALID_ACCESS_CODE유효하지 않은 Access Code입니다올바른 Access Code를 입력하세요.
4003002ACCESS_CODE_ALREADY_USED이미 사용된 Access Code입니다새로운 Access Code를 발급받아 사용하세요.
4003003ACCESS_CODE_EXPIRED만료된 Access Code입니다새로운 Access Code를 발급받아 사용하세요.
4003010ACCESS_CODE_NOT_VALIDATEDAccess Code 검증이 완료되지 않았습니다회원가입 전에 Access Code 검증 과정을 완료하세요.
4041100TERMS_NOT_FOUND약관을 찾을 수 없습니다약관 ID를 확인하고 다시 시도하세요.
4001101INVALID_TERMS_STATUS유효하지 않은 약관 상태입니다약관 상태 값을 확인하고 올바른 값을 입력하세요.
4041102TERMS_TRANSLATION_NOT_FOUND약관 번역을 찾을 수 없습니다언어 코드와 약관 ID를 확인하세요.
4001103REQUIRED_TERMS_NOT_AGREED필수 약관에 동의하지 않았습니다필수 약관에 동의 후 다시 시도하세요.
4091104DUPLICATE_TERMS_TRANSLATION이미 존재하는 약관 번역입니다다른 언어로 번역을 추가하거나 기존 번역을 업데이트하세요.
4001105ACTIVE_TERMS_ALREADY_EXISTS동일한 유형의 활성화된 약관이 이미 존재합니다기존 활성 약관을 비활성화한 후 다시 시도하세요.
4041106TERMS_AGREEMENT_NOT_FOUND약관 동의 정보를 찾을 수 없습니다사용자 ID와 약관 ID를 확인하세요.
4092020EMAIL_ALREADY_EXISTS이미 사용 중인 이메일입니다다른 이메일 주소를 사용하거나 로그인을 시도하세요.
4042021EMAIL_NOT_FOUND존재하지 않는 이메일입니다이메일 주소를 확인하거나 회원가입을 진행하세요.
4222027UNREGISTERED_EMAIL가입되지 않은 이메일입니다이메일 주소를 확인하거나 회원가입을 진행하세요.

변경 이력

버전날짜작성자변경 내용
0.1.02025-03-01bok@weltcorp.com최초 작성
0.2.02025-03-25bok@weltcorp.com초기 버전 작성
0.3.02025-04-11bok@weltcorp.com앱 인증 API 엔드포인트 이름 변경 (verify → complete-challenge, validate → verify-token)
0.4.02025-04-11bok@weltcorp.com앱 토큰 검증 API 제거, 각 API에서 토큰 검증하도록 간소화
0.5.02025-04-11bok@weltcorp.com앱 토큰 조회 API 제거, 섹션 번호 조정
0.6.02025-04-30bok@weltcorp.com회원가입용과 비밀번호 재설정용 이메일 인증 코드 발송 API 분리
0.7.02025-05-07bok@weltcorp.com내부 운영자(operation user) 인증 관련 API 추가
0.8.02025-05-07bok@weltcorp.comUNREGISTERED_EMAIL 오류 코드 추가 (422 HTTP 상태 코드)
0.9.02025-05-08bok@weltcorp.com이메일 인증 API에 타입 필드 추가 (REGISTRATION, LOGIN, PASSWORD_RESET), /auth/terms-and-consents 경로를 /auth/agreements로 변경
0.9.12025-05-08bok@weltcorp.com별도 비밀번호 재설정용 이메일 인증 API 제거, 통합 이메일 인증 API로 일원화
0.9.22025-05-15bok@weltcorp.comREGISTRATION_VALIDATION_FAILED (2028) 오류 코드 추가
0.10.02025-07-28bok@weltcorp.com등록 프로세스 중단 API 추가 (DELETE /v1/auth/registration/abort), deviceId 기반 캐시 정리 지원, 관련 오류 코드 추가 (2071-2073)
0.11.02025-08-11bok@weltcorp.comJWT stateless 인증 방식에 맞게 API 간소화: 로그아웃, 토큰 갱신 API에서 deviceId 파라미터 제거 (JWT에서 자동 추출)
0.12.02025-10-27bok@weltcorp.com게스트 온보딩/Step-up API 추가 및 관련 오류 코드 확장