본문으로 건너뛰기
버전: 개발 버전 (최신)

Native 2FA 회원가입 API (v2)

eID 대신 P-256 디바이스 키로 가입하는 v2 endpoint. Plan 209 로 가입은 단일 POST /v2/auth/register/native 한 번으로 끝나며, 공개키(SPKI)만 등록한다. 가입 시점의 self-POP 서명(디바이스 키 소유 증명)은 제거됐고, 키 소유 증명은 첫 로그인에서 수행한다.

사전 단계(App Token / 약관 / Email OTP / AppCheck)는 eID 회원가입과 완전히 동일 — 본 페이지에서는 anchor 링크로 참조한다.

인증 정책 (v2 EU 공통)
  • 인증 채널은 Authorization: Bearer 한 줄.
  • deviceId 는 token payload 에서. body 에 deviceId 를 보내면 400 VALIDATION_ERROR.
  • signup → 로그인 세션. 응답은 login/native 와 동일한 LoginV2ResponseDto(tokens + user + profile + roles + permissions + agreements). 클라이언트는 access token 을 저장해 바로 (재)로그인하거나 그대로 사용한다 — 가입 직후 별도 first-login 을 반복하지 않는다.
  • 가입 = 공개키만 등록(Plan 209). 디바이스 키 소유 증명(서명)은 가입이 아니라 로그인 전담 — nonce TTL 만료로 인한 가입 실패 모드가 사라졌다.
  • 마스터 문서: Auth 도메인 endpoints (Native 2FA 회원가입).

0. 호출 순서

#APIAuthorizationbody 핵심응답에서 추출할 값
1POST /v2/auth/app/challenge없음{ "deviceId" (object), "deviceIdHash" }challenge, nonce
2POST /v2/auth/app/complete-challenge없음{ "deviceIdHash", "encryptedChallenge" }appToken
3GET /v2/mobile/app-settings없음 (User-Agent 필수)약관 versionId 목록
4POST /v2/auth/email/verification-codeBearer <appToken>{ "email", "type": "REGISTRATION" }requestId
5POST /v2/auth/email/verifyBearer <appToken>{ "email", "code", "requestId", "type": "REGISTRATION" }verificationId
6POST /v2/security/appcheck/verifyBearer <appToken>{ "token", "platform" }{ verified: true }
7POST /v2/auth/register/nativeBearer <appToken>{ "devicePublicKeySpki", "email", "emailVerificationId", "agreements", "profile" }LoginV2ResponseDto (tokens + user)

①~⑥ 은 eID 회원가입과 완전히 동일한 공통 게이트. 디바이스 비밀키로 서버 nonce 를 서명하는 단계(self-POP)는 더 이상 없다 — 클라이언트는 디바이스 공개키(SPKI)만 ⑦ body 에 실어 보낸다.


1. POST /v2/auth/register/native

디바이스 공개키 SPKI + 약관·이메일 OTP 검증 ID 를 받아 User+Profile+ACTIVE 디바이스 키를 등록하고, login/native 와 동일한 LoginV2ResponseDto 세션을 반환한다 — 가입 직후 별도 first-login 을 반복하지 않는다.

  • Path: POST /v2/auth/register/native
  • 인증: App Token 필요 (app token 검증)
  • Rate Limit: 5/분 (prod 상시 적용; dev/stage/local은 THROTTLE_DISABLED=true 설정 시에만 해제)
  • 🔗 라이브 명세 (Swagger UI): dev

Request

HeaderValue
AuthorizationBearer <appToken>
Content-Typeapplication/json
{
"devicePublicKeySpki": "MFkwEwYHKoZIzj0CAQ...public-key-spki-base64url",
"email": "user@example.com",
"emailVerificationId": "verif_uuid_from_email_verify",
"agreements": [
{
"versionId": "agv_xxx",
"isAgreed": true
}
],
"profile": {
"userName": "Erika Mustermann",
"language": "de-DE",
"timezone": {
"id": "Europe/Berlin",
"offsetInMinutes": 60
}
}
}
필드타입필수설명
devicePublicKeySpkistringYes디바이스 공개키 SPKI DER 를 base64url 인코딩 (P-256 EC). iOS 는 x963→SPKI 변환 후 보낸다. 서명(self-POP)은 보내지 않는다.
emailstringYes사용자 이메일.
emailVerificationIdstringYesC (type=REGISTRATION) 가 반환한 verificationId.
agreementsarrayYes약관 동의 항목 (eID 가입과 동일 형식).
profileobjectNo사용자 프로필 (eID 가입과 동일 형식). language, userName, timezone: { id, offsetInMinutes } (모두 옵션). timezone 생략 시 Europe/Berlin(+60) 기본값.
body 에 받지 않는 필드

deviceId, appCheckToken, platform, signature 는 body 에 받지 않는다. 특히 signature 는 Plan 209 로 제거됐다 — 보내면 unknown 필드로 400 VALIDATION_ERROR.

Response 201 Created — 로그인 세션 (LoginV2ResponseDto)

login/native 와 동일한 형태. REGISTERED 단계라 userCycle 은 생략되고 roles/permissions 는 빈 배열 — 서비스 활성화(POST /v2/auth/user-cycle/activate) 후 채워진다.

{
"tokens": [
{
"token": "<access-jwt>",
"type": "ACCESS_TOKEN",
"expiresIn": 3600,
"issuedAt": 1714523700000
},
{
"token": "<refresh-jwt>",
"type": "REFRESH_TOKEN",
"expiresIn": 1209600,
"issuedAt": 1714523700000
}
],
"user": {
"id": "c1e7c5cf-6fc2-4f4f-8e2f-9b8a3c5e2d1b",
"email": "user@example.com",
"questionnaireBundleId": "default-bundle-id",
"createdAt": 1714523700000
},
"profile": {
"language": "de-DE",
"timezone": {
"id": "Europe/Berlin",
"offsetInMinutes": 60
},
"userName": "Erika Mustermann"
},
"roles": [],
"permissions": [],
"agreements": []
}

cohort/region/sessionPolicy 는 응답 body 에 포함되지 않는다(가입 시점엔 항상 standard/EU 로 고정이라 생략). cohort 분기는 서비스 활성화 시점에 결정된다.

DB 영향

  • 디바이스 키 등록 (status=ACTIVE).
  • 사용자 레코드 생성 (service_state=REGISTERED, region=EU, cohort=standard).

Errors

HTTPcodemessage발생 조건
4001001VALIDATION_ERRORbody 필드 누락/형식 오류 (예: devicePublicKeySpki 누락) 또는 unknown 필드 포함 (signature 등)
4002008EMAIL_NOT_VERIFIEDemail+emailVerificationId 가 REGISTRATION OTP 캐시와 불일치/만료
4002215NATIVE2FA_PUBLIC_KEY_INVALIDdevicePublicKeySpki SPKI 파싱 실패 또는 P-256 EC 키 아님
4011000App token not provided / Missing deviceId claim in app token payloadapp token 검증 실패 또는 payload 에 deviceId 누락
4092024DUPLICATE_EMAIL이미 가입된 이메일
4122080REQUIRED_AGREEMENTS_NOT_AGREED필수 약관 미동의
4291000ThrottlerException: Too Many RequestsEndpoint throttle 5/분 (prod 상시 적용)

Plan 209 로 self-POP 가 제거되면서 가입 단계의 2213 NATIVE2FA_CHALLENGE_INVALID(nonce 만료/서명 실패)는 더 이상 발생하지 않는다.


다음 단계