서비스 활성화 API (v2)
서비스 활성화 API는 회원가입 완료(REGISTERED) 상태의 사용자가 AccessCode(처방/발급 코드)를 등록해 치료 서비스를 시작(SERVICE_STARTED)하는 v2 endpoint입니다. 가입과 서비스 시작이 분리된 Plan 200 설계에 따라, 가입 직후 발급된 토큰에는 uci(user cycle id)와 userCycle이 없으며, 활성화가 이를 채운 새 세션을 재발급합니다.
- "회원가입 완료(REGISTERED) ≠ 서비스 시작(SERVICE_STARTED)". 가입 흐름과 분리된 별도 게이트입니다.
- 활성화는 AccessCode 검증 + 소비(one-time) → entitlement(group/plan) 부여 → cohort/region 결정 → REGISTERED → SERVICE_STARTED 전이 → 새 access/refresh 세션 재발급(
LoginV2ResponseDto)을 한 번에 수행합니다. - 응답은 다른 v2 인증 endpoint와 동일한
LoginV2ResponseDto계열(ActivateServiceResponseDto)이며, 이때부터userCycle이 채워지고 access token payload에uci가 박힙니다. cohort/region은 응답 body가 아니라 **access token의identityBindings**에 실립니다.
- 인증 채널은
Authorization: Bearer <accessToken>한 줄(JwtAuthGuard). deviceId는 access token payload에서 가져옵니다 — body로 보내면 400.
POST /v2/auth/user-cycle/activate ⭐
REGISTERED 사용자가 AccessCode를 등록해 치료 서비스를 시작합니다.
- Path:
POST /v2/auth/user-cycle/activate - 인증: User Access Token (
JwtAuthGuard) - Rate Limit: 5회/분(endpoint throttle, BSI O.Auth_7) + AccessCodeValidatorService의 디바이스당 10회/시간 잠금이 함께 적용
- 🔗 라이브 명세 (Swagger UI): dev —
Auth - 9 - 공통/부가태그
Request
| Header | Value |
|---|---|
Authorization | Bearer <accessToken> (필수) |
Request Body — ActivateServiceDto
| 필드 | 타입 | 필수 | 제약 | 설명 |
|---|---|---|---|---|
accessCode | string | Yes | ^[A-Z0-9-]{8,32}$ (대문자/숫자/-) | 처방·발급된 AccessCode |
deviceId는 access token payload(request.user.deviceId)에서 가져오므로 body에 포함하면 안 됩니다. rate-limit 키를 인증된 세션과 일치시키기 위한 설계입니다.
{
"accessCode": "ABCD-1234-EFGH"
}
동작
JwtAuthGuard로 access token을 검증하고 payload에서userId·deviceId를 확정합니다.- AccessCode를 검증·소비(one-time)합니다 — 존재/만료/재사용 여부 확인.
- entitlement(group/plan)를 부여하고 cohort/region을 결정합니다.
- 사용자 상태를 REGISTERED → SERVICE_STARTED로 전이합니다.
uci가 박힌 새 access/refresh 세션을 발급해LoginV2ResponseDto형태로 반환합니다.
Response 200 OK — ActivateServiceResponseDto
ActivateServiceResponseDto는 LoginV2ResponseDto(= v1 LoginResponseDto와 동일 구조)를 상속합니다. 활성화 직후 사용자는 "로그인된 상태"로 진입하므로 tokens·user·userCycle·roles·permissions·profile·agreements를 한 번에 받습니다.
| 필드 | 타입 | 설명 |
|---|---|---|
tokens | TokenResponseDto[] | access token + refresh token (정확히 2개). cohort/region/uci는 access token의 identityBindings에 포함 |
user | UserDto | 사용자 정보 (id, email, questionnaireBundleId, createdAt, suspensionEndAt?) |
userCycle | UserCycleDto | 활성화 후 채워짐 — id, status, startedAt, count, treatmentDurationDays |
profile | UserProfileDto | language, timezone{id, offsetInMinutes}, userName? |
roles | string[] | 사용자 역할 목록 (예: healthcare.patientUser) |
permissions | string[] | {domain}.{resource}.{action} 형식 권한 목록 |
agreements | UserAgreementItemDto[] | 사용자 동의 항목 목록 |
응답 body 전체 구조는 v2 로그인 응답과 동일합니다 — 필드별 상세는 eID 로그인 / Native 2FA 로그인의
LoginV2ResponseDto표를 참조하세요.
Errors
| HTTP | code | message | 발생 조건 |
|---|---|---|---|
| 400 | 1001 | VALIDATION_ERROR | accessCode가 ^[A-Z0-9-]{8,32}$에 불일치, 또는 body에 deviceId 포함 |
| 400 | 3001 | INVALID_CODE | AccessCode를 찾을 수 없음 (코드 노출 방지를 위해 검증 실패로 처리) |
| 400 | 3003 | CODE_EXPIRED | AccessCode 만료 |
| 401 | 1000 | Authentication token not found | Bearer access token 미제공 (JwtAuthGuard) |
| 401 | 1000 | Invalid or expired token | access token 만료/위조 (JwtAuthGuard) |
| 401 | 1000 | Missing authenticated user | payload에 userId 없음 / request.user 부재 |
| 401 | 1000 | Missing deviceId claim in access token payload | access token payload에 deviceId claim 없음 |
| 409 | 3002 | CODE_ALREADY_USED | AccessCode 재사용 |
| 409 | 2240 | SERVICE_ALREADY_STARTED | 이미 SERVICE_STARTED 상태 |
| 409 | 2231 | AUTH_METHOD_CONFLICT | clinical AccessCode인데 Native 2FA 보유 → eID 전환 필요(clinical_requires_eid) |
| 429 | 3045 | RATE_LIMIT_EXCEEDED | AccessCodeValidatorService 잠금 (10회/시간/기기, metadata.remainingLockoutSeconds 포함) |
| 429 | 1000 | ThrottlerException: Too Many Requests | endpoint throttle 초과 (5회/분, BSI O.Auth_7) |
GET /v2/auth/user-cycle/state
사용자의 서비스 라이프사이클 상태를 조회합니다. DB read만 수행하므로 도메인 오류가 없으며, 사용자 레코드가 없으면 REGISTERED로 응답합니다.
- Path:
GET /v2/auth/user-cycle/state - 인증: User Access Token (
JwtAuthGuard)
Request
| Header | Value |
|---|---|
Authorization | Bearer <accessToken> (필수) |
Response 200 OK — ServiceStateResponseDto
{
"serviceState": "REGISTERED"
}
| 필드 | 타입 | 설명 |
|---|---|---|
serviceState | 'REGISTERED' | 'SERVICE_STARTED' | 현재 서비스 라이프사이클 상태 |
Errors
| HTTP | code | message | 발생 조건 |
|---|---|---|---|
| 401 | 1000 | Authentication token not found | Bearer access token 미제공 (JwtAuthGuard) |
| 401 | 1000 | Invalid or expired token | access token 만료/위조 (JwtAuthGuard) |
| 401 | 1000 | Missing authenticated user | payload에 userId 없음 / request.user 부재 |
다음 단계
- 활성화 전 단계의 진입 흐름: eID 로그인 · Native 2FA 로그인 (첫 로그인 직후 → 서비스 활성화).
- 활성화 후 일일 학습 콘텐츠: 일일 콘텐츠 조회.
- 도메인 관점의 흐름·상태 전이: Auth 도메인 endpoints.