게스트 Step-up 인증 및 identityLevel 확장 설계
개요
게스트 계정 확장 요구사항에 따라 Auth·IAM 경계에서는 아래 사항을 충족해야 합니다.
- 이메일/카카오 연동 없이 약관 동의만으로 발급되는 게스트 토큰의 안전한 수명 관리
- 정규 인증으로 승격(이하 Step-up) 시 기존 게스트 세션을 폐기하고 신규 신원 수준을 반영하는 계약
- Access/Refresh Token, 권한 평가, 이벤트 기반 싱크에서
identityLevel을 일관되게 사용 - 타 도메인(User, Group, Plan)과의 상태 전이를 비동기 이벤트로 확정
본 문서는 토큰 스키마, API 계약, 이벤트 흐름, IAM 연동 지침을 정의합니다.
핵심 설계 원칙: 게스트 아키텍처 (v1.2.0)
중요: 게스트 계정도 정식 사용자와 동일하게
User+UserCycle레코드를 생성합니다. 게스트와 정식 사용자의 구분은accountType필드가 아닌 IAM Group과 Plan으로 관리합니다.
게스트 등록 시 생성되는 데이터
| 데이터 | 설명 |
|---|---|
User | 게스트도 정식 User 레코드 생성 (UUID 발급) |
UserCycle | 치료 사이클 관리를 위한 UserCycle 생성 |
GuestAccountLifecycle | 게스트 생명주기 이벤트 기록 (만료, 업그레이드 추적) |
| IAM Group 할당 | guests 또는 guests.{region} 그룹 할당 |
| Plan 할당 | plan.guest 플랜 할당 |
Step-up 시 변경 사항 (userId 유지)
| 항목 | 변경 전 | 변경 후 |
|---|---|---|
| userId | abc-123 | abc-123 (유지) |
| IAM Group | guests.kr | patients.kr, patients.general (예시) |
| Plan | plan.guest | plan.therapeutic (예시) |
| identityLevel | guest | registered |
| 기존 데이터 | 수면 기록, 설문 등 | 그대로 연결됨 (userId 동일) |
이 설계의 핵심 장점은 Step-up 시 userId가 변경되지 않아 기존 데이터가 자연스럽게 연결된다는 것입니다.
IAM Group 체계
게스트와 정식 사용자의 구분은 IAM Group으로 관리합니다.
게스트 그룹 (guests.*)
| 그룹 | 설명 |
|---|---|
guests | 게스트 기본 그룹 |
guests.kr | 한국 지역 게스트 |
guests.de | 독일 지역 게스트 |
guests.us | 미국 지역 게스트 |
guests.jp | 일본 지역 게스트 |
정식 사용자 그룹 (Step-up 후)
Step-up 후 할당되는 그룹은 사용 목적에 따라 다양합니다. patients.*는 대표적인 예시일 뿐이며, 비즈니스 요구사항에 따라 다른 그룹으로 할당될 수 있습니다.
| 그룹 패턴 | 설명 | 예시 |
|---|---|---|
patients.* | 일반 치료 환자 그룹 | patients.kr, patients.de, patients.general |
clinical-trial.* | 임상시험 참가자 그룹 | clinical-trial.study-001, clinical-trial.phase-2 |
research.* | 연구 대상자 그룹 | research.sleep-study, research.pilot |
enterprise.* | 기업 고객 그룹 | enterprise.company-a, enterprise.wellness |
special.* | 특수 목적 그룹 | special.beta-tester, special.vip |
중요: 게스트 여부 판별은
guests.*그룹 소속 여부로만 판단합니다. 정식 사용자는 "특정 그룹에 속함"이 아니라 *"guests. 그룹에 속하지 않음"**으로 판별합니다.
// 게스트 판별 로직 (올바른 예)
const isGuest = roles.some(role => role.startsWith('guests.'));
// 잘못된 예 - patients만 정식 사용자로 간주
// const isRegistered = roles.some(role => role.startsWith('patients.')); // ❌
토큰 스키마 확장
| 항목 | 설명 | 게스트 | 정규 사용자 |
|---|---|---|---|
identityLevel (Access Token Claim) | 신원 검증 수준. IAM 정책 계산 및 기능 Gate에 사용 | guest 고정 | registered 또는 상위 수준(OAuth, 2FA 등) |
consents | 약관·선택 동의 집합. 게스트는 필수 약관만 보유 | 필수 약관 최소화 | 정규 프로필 기준 |
identityBindings (Refresh Token Meta) | 연결된 인증 수단 목록(email, kakao 등) | 빈 배열 | 적어도 하나 이상 |
deviceBinding | 토큰을 발급한 App Token/Device ID | 최초 온보딩 디바이스로 고정 | 마지막 인증 디바이스로 갱신 가능 |
- Access Token 예시 (요약):
{
"sub": "550e8400-e29b-41d4-a716-446655440000",
"identityLevel": "guest",
"guestAccountId": "guest-4f52c7fa",
"groupIds": ["guests.kr"],
"planId": "plan.guest",
"consents": ["terms.v1.accepted"],
"deviceBinding": "appToken:abc123",
"exp": 1692698400,
"iat": 1692697500,
"aud": ["dta-wide-api"]
}
참고:
sub필드는 User 테이블의 UUID입니다. 게스트도 정식 User 레코드가 생성되므로 UUID가 발급됩니다.guestAccountId는 게스트 생명주기 추적을 위한 별도 식별자입니다.
- Refresh Token 메타 예시:
{
"identityLevel": "guest",
"identityBindings": [],
"deviceBinding": "device:5f92c1"
}
토큰 저장소는 identityLevel과 deviceBinding을 키로 함께 인덱싱하여 게스트 토큰 재사용을 감시합니다.
API 계약
1. 게스트 등록 (POST /auth/guest/register)
- 입력: App Token, User-Agent 헤더, 디바이스 정보 (uuid), 약관 동의 목록 (agreements), 프로필 정보 (profile).
- 검증:
- AppTokenGuard로 앱 무결성 확인.
- 이미 등록된 디바이스와의 중복 여부 확인.
- 지역별 동의 정책(Group/Plan 의존) 충족.
- User-Agent 헤더에서 region, platform, appVersion 등 자동 추출.
profile.language및profile.timezone필수 검증.
- 처리 (v1.2.0 업데이트):
- User 레코드 생성: UUID 발급, 기본 프로필 설정
- UserCycle 레코드 생성: 치료 사이클 관리 시작
- GuestAccountLifecycle 이벤트 기록:
ONBOARDED이벤트 - IAM Group 할당: User-Agent에서 추출한 region에 따라
guests.{region}그룹 할당 - Plan 할당:
plan.guest플랜 할당 - 만료 타이머 스케줄링: TimeMachine/Cloud Scheduler로 서비스 설정에 따른 만료 예약
- 출력:
- Access Token (
identityLevel=guest,sub=userId, TTL ≤ 15분) - Refresh Token (TTL ≤ 12시간)
userId(User 테이블의 UUID - Step-up 이후에도 유지됨)guestAccountId(게스트 생명주기 추적용)
- Access Token (
- 감사 로그:
auth.guest.registered(userId, guestAccountId, region, agreements, profile)
2. Step-up 인증 준비 (POST /auth/guest/link/init)
- 게스트가 이메일 또는 카카오 연동을 선택하면, Auth는 적절한 1차 인증(이메일 코드, OAuth state) 플로우를 시작합니다.
- 출력:
linkSessionId, 만료 시간, 선택된 인증 수단. - 제약: 게스트 토큰과 동일 디바이스에서만 호출 가능.
3. Step-up 인증 완료 (POST /auth/guest/link/complete)
- 입력:
linkSessionId, 인증 증명(이메일 코드/카카오 Authorization Code 등), 최신 Refresh Token. - 처리 (v1.2.0 업데이트 - userId 유지):
- 인증 증명 검증 → 신규
credentialId발급. - 기존 Refresh Token과 Access Token을 모두 블랙리스트 처리.
- User 도메인에
user.identity-level-upgrade.requested명령 이벤트 발행. - Group 변경:
guests.{region}→patients.{region},patients.general - Plan 변경:
plan.guest→plan.therapeutic - GuestAccountLifecycle 기록:
UPGRADED이벤트 - 만료 타이머 해제: 게스트 만료 스케줄 취소
- 신규 Access/Refresh Token 발급 (
identityLevel=registered, 동일한sub=userId).
- 인증 증명 검증 → 신규
- 핵심: userId(UUID)는 변경되지 않음 - 게스트 기간 동안 생성된 모든 데이터가 자동으로 연결됨
- 출력: 신규 토큰 페어 (동일 userId), 업그레이드 요약(필수 약관 추가 여부, 다음 단계 안내).
4. Link 해제 (POST /auth/guest/link/cancel)
- 인증 진행 중인 세션을 취소하고,
linkSessionId와 연계된 임시 리소스를 정리합니다.
시퀀스 다이어그램
게스트 등록 플로우 (v1.2.0)
Step-up 인증 플로우 (v1.2.0 - userId 유지)
이벤트 계약
| 이벤트 | 발행자 | 목적 | 필드 |
|---|---|---|---|
auth.guest.registered | Auth | 게스트 생성 감사 및 분석 (v1.2.0: userId 포함) | userId, guestAccountId, region, deviceFingerprint, agreements, profile, groupIds, planId, timeMachineTick |
user.identity-level-upgrade.requested | Auth | User 도메인에 Group/Plan 전환 명령 (userId 유지) | userId, guestAccountId, credentialType, credentialId, targetGroupIds, targetPlanId, timeMachineTick |
user.identity-level-upgrade.confirmed | User | Auth에 게스트→정규 사용자 전환 확정 통보 (동일 userId) | userId, previousGroupIds, newGroupIds, previousPlanId, newPlanId, timeMachineTick |
auth.identity-level.changed | Auth | IAM·Plan·Group 캐시 동기화 | userId, identityLevel, bindings, planId, groupIds, revokedRefreshTokens |
iam.access-cache.invalidate | IAM | 권한 캐시 무효화 트리거 | userId, invalidateReason, timeMachineTick |
v1.2.0 변경사항:
auth.guest.registered이벤트에userId가 추가되었습니다. 게스트 등록 시점에 User 레코드가 생성되므로 모든 이벤트에서 동일한 userId를 사용합니다.
모든 이벤트는 TimeMachineService의 tick 값을 포함해 재현 가능한 타임라인을 보장합니다.
IAM 연동 지침
-
권한 평가 입력 확장
AccessEvaluationRequest에identityLevel과planId,groupIds를 함께 전달합니다. 게스트 토큰의 경우planId=plan.guest.default형태로 관리합니다. -
정책 표현식
- 게스트 차단 자원:
requiresIdentityLevel >= registered - 게스트 허용 자원:
identityLevel == guest && permission in guestAllowList
- 게스트 차단 자원:
-
캐시 무효화
auth.identity-level.changed수신 시 사용자별 권한 캐시를 전량 폐기합니다.revokedRefreshTokens목록을 통해 기존 세션을 추적하고, API Gateway 레벨에서 블랙리스트 반영.
-
감사 로깅
게스트 승격 시iam.audit로그에 이전/이후 권한 스냅샷을 저장하여 추적성을 확보합니다.
보안 고려사항
- 게스트 Refresh Token은 단일 디바이스에서만 갱신 가능하도록
deviceBinding을 검증합니다. - Step-up 완료 시, 게스트 토큰은 즉시 블랙리스트에 추가하고 TTL이 만료되기 전이라도 사용을 거부합니다.
- 동일 디바이스에서 3회 이상 Step-up 실패 시 계정을
LOCKED상태로 전환하며, 추가 인증 절차(고객센터) 안내를 반환합니다. - OAuth Step-up의 경우 state 파라미터에
guestAccountId를 포함하되, 서명된 JWT 형태로 전달해 위변조를 방지합니다.
미해결 과제
- 헬스케어 규제에 따른 게스트 상태 허용 범위(예: 진단 데이터 접근) 세부 정의.
- Step-up 실패 시 재시도 백오프 정책과 UI 메시지 표준화.
- 게스트와 정규 사용자 간 동의 항목 차등 적용에 대한 Legal 검토.
구현 및 테스트 계획
1. 구현 단계
- 데이터 모델 확장 (v1.2.0 업데이트)
- User: 게스트도 정식 User + UserCycle 레코드 생성.
GuestAccountLifecycle테이블로 생명주기 추적. - Plan:
plan.guest플랜 생성 (서비스 정책에 따른 제한된 기능 및 이용 기간). - Group:
guests,guests.kr,guests.de,guests.us,guests.jp그룹 시드 데이터 생성.
- User: 게스트도 정식 User + UserCycle 레코드 생성.
- Auth 서비스 업데이트
guest/register,guest/link/*API 구현.- identityLevel 클레임 포함 Access/Refresh Token 발급.
- Step-up 완료 시
auth.identity-level.changed이벤트 발행.
- User/Group/Plan/IAM 연동
- User: 게스트 만료 타이머 스케줄링, Step-up 수신 핸들러, 게스트 정리 워커 구현.
- Group: 게스트 자동 할당, Step-up 이관, 만료 정리 핸들러 구현.
- Plan: 게스트 플랜 할당/전환, 만료 정리 로직 구현.
- IAM: identityLevel 기반 인가 평가 및 캐시 무효화 구현.
- Backlog 태스크 매핑
task-1 Implement guest plan/group automationtask-2 Automate guest expiry and upgrade nudges- 추가 태스크: IAM identityLevel 평가, Auth API 구현, Frontend Step-up UX (추가 생성 필요)
2. 테스트 전략
| 레벨 | 범위 | 주요 시나리오 |
|---|---|---|
| 단위(Unit) | Auth/Plan/Group/IAM 서비스 | 게스트 토큰 발급, Step-up 완료, 권한 평가, 만료 정리 로직 |
| 통합(Integration) | Auth ↔ User ↔ Group ↔ Plan ↔ IAM | auth.guest.onboarded부터 Step-up 완료, 만료 정리까지 이벤트 흐름 |
| E2E | 모바일 앱+백엔드 | 게스트 온보딩 → 기능 제한 → Step-up → 정규 기능 접근, 만료 후 접근 차단 |
| 회귀(Regressions) | 기존 사용자 가입/로그인 | 기존 플로우가 영향받지 않는지 확인 |
3. 검증 체크리스트
구현 완료 (v1.2.0)
- 게스트 토큰이
identityLevel=guest로 발급되고 권한 평가에서 제한되는지 확인 - TimeMachine 기반 만료 이벤트가 예정 시간에 실행되는지 확인 (GuestExpirySchedulerService)
- Audit 로그 및
GuestAccountLifecycle레코드가 누락 없이 생성되는지 확인 - (v1.2.0) 게스트 등록 시 User + UserCycle 레코드가 정상 생성되는지 확인 (GuestCreationService)
- (v1.2.0) 게스트 자발적 탈퇴 시 만료 타이머 해제 및 VOLUNTARILY_WITHDRAWN 이벤트 기록
TODO: Step-up 인증 구현 필요
- Step-up 완료 시 모든 게스트 토큰이 블랙리스트 처리되는지 확인
- User/Group/Plan에서 게스트 → 등록 전환 이력이 정확히 기록되는지 확인
- IAM
POST /v1/iam/access/evaluateAPI가 게스트와 정규 사용자 모두 정확한 결과를 반환하는지 확인 - (v1.2.0) Step-up 시 userId가 변경되지 않고 기존 데이터와 연결되는지 확인
- (v1.2.0) 게스트 그룹(
guests.*)이 정식 그룹으로 올바르게 변경되는지 확인
Step-up API 구현 TODO
POST /auth/guest/link/init - Step-up 인증 준비
POST /auth/guest/link/complete - Step-up 인증 완료
POST /auth/guest/link/cancel - Step-up 인증 취소
변경 이력
| 버전 | 날짜 | 작성자 | 변경 내용 |
|---|---|---|---|
| 1.0.0 | 2025-04-20 | bok@weltcorp.com | 최초 문서 생성 |
| 1.1.0 | 2025-05-15 | bok@weltcorp.com | Step-up 인증 플로우 상세화 |
| 1.2.0 | 2025-12-17 | bok@weltcorp.com | 게스트 아키텍처 개선: 게스트도 User+UserCycle 생성, IAM Group 기반 분류(guests.*), Plan 기반 기능 제어(plan.guest), Step-up 시 userId 유지 |
| 1.2.1 | 2025-12-18 | bok@weltcorp.com | 구현 상태 반영: 게스트 등록/만료/자발적 탈퇴 완료, Step-up 인증 TODO 명시 |
| 1.2.2 | 2025-12-18 | bok@weltcorp.com | IAM Group 체계 문서화: 정식 사용자 그룹 다양성 명시 (patients.* 외 clinical-trial., research., enterprise., special. 등), 게스트 판별 로직 가이드 추가 |