워터폴 플로우
k-ID를 사용한 연령 확인은 사용자가 개인정보를 공개하지 않고 연령을 증명할 수 있도록 하는 개인정보 보호 프로세스입니다. 이 접근 방식은 워터폴 플로우 모델을 사용합니다.
워터폴 플로우
AgeKit+는 연령 확인의 단일 지점 오케스트레이터로 작동하여 사용자의 연령을 확인하기 위해 확인 제공업체의 워터폴을 자동으로 연쇄합니다. 실제로 k-ID에 대한 하나의 API 호출이 구성된 방법을 순차적으로 제시합니다. 예를 들어 이메일 추론이나 얼굴 연령 추정으로 시작한 다음 필요에 따라 ID 문서 스캔이나 다른 방법으로 대체하여 사용자의 연령이 확인되거나 모든 옵션이 소진될 때까지 진행합니다. 이는 개발자가 k-ID의 API와 한 번 통합하고 플랫폼이 배후에서 여러 확인 기술을 시도하여 방법을 결합하여 확인 성공 가능성을 극대화함을 의미합니다.
확인 흐름은 iframe이나 모바일 웹 보기에 호스팅될 URL을 반환하는 API 호출로 시작되며, 사용자가 확인 프로세스를 완료합니다. 사용 가능한 확인 방법은 Compliance Studio의 제품 구성에 의해 결정되어 관할권 요구 사항을 준수합니다.
| API | 시나리오 |
|---|---|
/age-verification/perform-access-age-verification | 기능, 성숙한 콘텐츠 또는 제품 자체에 액세스하기 전에 사용자의 연령을 확인합니다. |
/age-verification/perform-trusted-adult-verification | 신뢰할 수 있는 성인(부모 또는 보호자) 확인을 수행합니다. |
/age-verification/perform-age-appeal | 연령 확인에 실패했지만 결정에 대해 항소하려는 사용자를 위한 것입니다. |
연령 확인 API는 요청 및 응답 형식 측면에서 표준화되어 있습니다.
요청 본문
| 속성 | 설명 | 필수? |
|---|---|---|
jurisdiction | 연령 확인이 수행되어야 하는 관할권 | 예 |
criteria | 연령 확인을 위한 기준 | 예 |
subject.email | 사용자가 이메일 주소로 다른 컨텍스트에서 k-ID로 연령을 확인한 경우 사용자에게 다시 연령을 추정하거나 증명하도록 요청하는 대신 원래 연령이 반환됩니다. | 아니오 |
subject.claimedAge | 사용자가 연령 게이트에서 연령을 요청받은 경우 연령 추정 프로세스에 알리는 데 사용됩니다 | 아니오 |
subject.id | 여러 확인 방법에 걸쳐 여러 실패 시도를 보고하는 데 사용되는 식별자입니다. 임시 세션 ID 또는 해시된 사용자 ID일 수 있습니다. | 아니오 |
options.facialAgeEstimation.passIfOver | 얼굴 연령 추정을 자동으로 통과하는 데 필요한 추정 연령 임계값입니다. 추정 연령이 이 값 이상인 경우 확인이 통과합니다. | 아니오 |
options.facialAgeEstimation.failIfUnder | 이 값 미만인 경우 확인이 실패합니다. 추정 연령이 이 값 미만인 경우 확인이 실패합니다. 생략된 경우 확인 기준 연령이 기본값입니다. | 아니오 |
options.redirectUrl | 확인 완료 후 리디렉션할 URL. HTTP/HTTPS URL 또는 사용자 지정 프로토콜 스킴을 사용한 모바일 딥 링크를 지원합니다. 리디렉션은 확인 URL이 브라우저 또는 웹 보기에서 직접 열릴 때만 발생합니다(iframe에 임베드되지 않은 경우). 리디렉션이 발생하면 URL에 verificationId와 result(PASS 또는 FAIL)가 쿼리 문자열 매개변수로 포함됩니다. | 아니오 |
passIfOver 및 failIfUnder 매개변수를 사용하면 얼굴 연령 추정 결과에서 허용되는 변동성을 제어할 수 있습니다. 얼굴 연령 추정 스캔이 수행되면:
- 추정 연령이
passIfOver** 이상**인 경우 확인이 통과하고 연령 신호가 결정됩니다. - 추정 연령이
failIfUnder** 미만**인 경우 확인이 실패하고 연령 신호가 결정됩니다. - 추정 연령이
failIfUnder와passIfOver** 사이**에 있는 경우 결과는 불확정으로 간주되며 사용자는 얼굴 연령 추정을 다시 시도할 수 있습니다.
이를 통해 결과가 명확하게 판단할 수 있는 신뢰 범위를 설정하면서 추정이 불확실한 범위에 해당하는 경우 사용자가 재시도할 수 있도록 합니다. 예를 들어 18세 이상의 사용자를 확인해야 하는 경우 passIfOver를 25로, failIfUnder를 12로 설정할 수 있습니다. 이는 25세 이상으로 추정된 사용자는 즉시 통과하고, 12세 미만으로 추정된 사용자는 즉시 실패하며, 12-24세로 추정된 사용자는 스캔을 다시 시도하거나 다른 확인 방법을 시도할 수 있음을 의미합니다.
샘플:
{
"jurisdiction": "US-CA",
"criteria": {
"ageCategory": "ADULT"
},
"options": {
"facialAgeEstimation": {
"passIfOver": 25,
"failIfUnder": 12
},
"redirectUrl": "https://example.com/verification-complete"
}
}
리디렉션 URL
redirectUrl 매개변수를 사용하면 확인 완료 후 사용자를 리디렉션할 위치를 지정할 수 있습니다. 이는 다음에 유용합니다:
- 브라우저 기반 흐름: 확인 완료 후 다른 웹 페이지로 리디렉션
- 사용자 지정 성공 화면: 자체 사용자 지정 성공 또는 실패 페이지 표시
- 모바일 앱 딥 링크: 사용자 지정 프로토콜 스킴(예:
myapp://verification-complete)을 사용하여 모바일 앱으로 제어 반환
리디렉션은 확인 URL이 브라우저 또는 웹 보기에서 직접 열릴 때만 발생합니다(iframe에 임베드되지 않은 경우). iframe에 임베드된 경우 확인 결과는 DOM 이벤트를 통해 전달됩니다.
리디렉션이 발생하면 리디렉션 URL에 다음 쿼리 문자열 매개변수가 포함됩니다:
verificationId: 고유한 확인 IDresult: 확인 결과,PASS또는FAIL
리디렉션 URL 예시:
https://example.com/verification-complete?verificationId=7854909b-9124-4bed-9282-24b44c4a3c97&result=PASS
응답 본문
연령 확인 API에 대한 성공적인 요청은 다음 응답을 반환합니다.
| 속성 | 설명 |
|---|---|
id | 연령 확인 서비스에서 생성한 고유 확인 ID |
url | iframe에 포함되어 사용자에게 제시되어야 하는 연령 확인 URL입니다. |
샘플:
{
"id": "7854909b-9124-4bed-9282-24b44c4a3c97",
"url": "https://family.k-id.com/verify?token=eyJ..."
}
확인 인터페이스 포함
반환된 URL을 사용하여 웹사이트나 앱에 iframe을 만듭니다. 사용자는 이 인터페이스를 통해 확인을 완료하며, 사용 가능한 방법은 관할권 요구 사항에 맞게 자동으로 조정됩니다.

<div id="verification-container">
<iframe
id="verification-widget"
src="VERIFICATION_URL"
width="100%"
height="600"
frameborder="0"
allow="camera;payment;publickey-credentials-get;publickey-credentials-create">
</iframe>
</div>
allow 속성은 다음 기능을 활성화하는 데 필요합니다:
camera: 얼굴 연령 추정에 필요payment: 신용카드 확인에 필요publickey-credentials-get및publickey-credentials-create: WebAuthn 기반 확인 방법에 필요
확인 결과
사용자가 연령 확인을 성공적으로 완료했거나 최대 재시도 횟수에 도달했지만 성공하지 못한 경우 연령 확인 결과는 클라이언트 측 및 서버 측 채널을 통해 전달됩니다. 구현은 둘 다의 조합을 사용해야 합니다: 클라이언트 측 이벤트는 UI 요소를 제어하는 데 가장 좋지만, 데이터 무결성을 위해 실제 결과는 웹훅이나 /age-verification/get-status 호출에서 가져와야 합니다.
클라이언트 측(DOM 이벤트) - 응답 본문의 URL이 iframe에 포함된 경우 Verification.Result 구조로 부모 프레임에 창 메시지(MessageEvent)로 전송됩니다.
서버 측(웹훅) - Verification.Result 이벤트 형식으로 등록된 웹훅에 이벤트가 전송됩니다.
창 메시지 액세스 예시:
const handleMessage = (event: MessageEvent) => {
const message = event.data;
if (message.eventType === "Verification.Result") {
// 즉시 UI 업데이트를 위해 DOM 이벤트 사용
updateUI(message);
}
};
window.addEventListener("message", handleMessage);
데이터 무결성을 위해 항상 웹훅의 이벤트나 /age-verification/get-status 호출로 결과를 확인하세요. DOM 이벤트에만 의존하지 마세요. DOM 이벤트는 반응형 UI 업데이트에 가장 적합합니다.
창 이벤트와 웹훅 이벤트의 데이터 요소에는 다음 속성이 포함됩니다.
| 속성 | 설명 |
|---|---|
id | 이것이 결과인 확인 ID입니다. |
status | 사용자가 연령 기준을 충족했는지 여부를 기반으로 PASS 또는 FAIL 상태를 나타냅니다. |
ageCategory | 요청에 지정된 관할권에서 사용자가 속한 연령 카테고리를 나타냅니다. 지원되는 값은 adult, digital-youth 또는 digital-minor입니다 |
method | 확인에 사용된 방법을 나타냅니다. 지원되는 값은 id-document, age-estimation, age-attestation, credit-card, social-security-number입니다 |
failureReason | 확인이 실패한 이유입니다. 지원되는 값은 age-criteria-not-met, max-attempts-exceeded 또는 fraudulent-activity-detected입니다. 이것은 status가 FAIL인 경우에만 설정됩니다 |
age | 추정되거나 확인된 연령의 하한 및 상한을 low 및 high로 반환합니다. |
샘플:
{
"eventType": "Verification.Result",
"data": {
"id": "5a58e98a-e477-484b-b36a-3857ea9daaba",
"status": "PASS",
"ageCategory": "adult",
"method": "id-document",
"age": {
"low": 25,
"high": 25,
}
}
}
창 이벤트 처리:
const handleMessage = (event: MessageEvent) => {
const message = event.data;
if (message.eventType === "Verification.Result") {
if (message.data.status === "PASS") {
window.location.href = `https://www.example.com/success?verificationId=${message.data.id}`
}
if (message.data.status === "FAIL") {
window.location.href = `https://www.example.com/fail?verificationId=${message.data.id}`
}
}
};
window.addEventListener("message", handleMessage);
확인 오류
예기치 않은 오류가 발생한 경우 JavaScript 이벤트가 발생하여 구현이 오류를 우아하게 처리할 수 있습니다.
이벤트 구조에 대한 자세한 내용은 Verification.Error를 참조하세요.
샘플 메시지:
{
"eventType": "Verification.Error",
"method": "credit-card",
"status": "ERROR"
}
확인 상태 확인
JavaScript와 k-ID 웹훅을 통해 이벤트를 보내는 것 외에도 확인 상태를 쿼리할 수도 있습니다. 이는 등록된 웹훅이 일정 기간 동안 도달할 수 없었고 상태 이벤트가 전송되지 않은 경우를 처리하는 데 유용합니다. 확인 상태를 가져오려면 /age-verification/get-status API를 사용하세요. 반환되는 데이터 구조는 Verification.Result 웹훅 이벤트와 동일한 계약을 따르지만, 구조 및 필드 존재에 몇 가지 차이점이 있습니다. 필드 존재 규칙, 상태 유형, 웹훅과 API 엔드포인트 응답의 차이점, 구현 가이드를 포함한 확인 결과 분석에 대한 자세한 내용은 확인 이벤트 계약을 참조하세요. 웹훅 이벤트에 대한 자세한 내용은 웹훅을 참조하세요.
엣지 케이스 처리
확인 결과를 처리할 때 확인 결과 및 실패 이유에 따라 응답 구조가 다를 수 있음을 고려해야 합니다. 다음 예제는 다양한 응답 변동에 대한 올바른 처리 패턴과 잘못된 처리 패턴을 보여줍니다. 완전한 필드 존재 규칙은 확인 이벤트 계약을 참조하세요.
부분적 시도 (최대 시도 횟수 초과)
사용자가 확정적인 연령 결정 없이 모든 확인 시도를 소진하면 확인은 max-attempts-exceeded로 실패합니다. 이 응답에는 method, age 또는 ageCategory 필드가 포함되지 않습니다.
예제 페이로드:
{
"eventType": "Verification.Result",
"data": {
"id": "123e4567-e89b-12d3-a456-426614174002",
"status": "FAIL",
"failureReason": "max-attempts-exceeded"
}
}
age-criteria-not-met 예제 페이로드 (비교용):
{
"eventType": "Verification.Result",
"data": {
"id": "123e4567-e89b-12d3-a456-426614174001",
"status": "FAIL",
"method": "age-estimation-scan",
"failureReason": "age-criteria-not-met",
"age": {
"low": 16,
"high": 17
}
}
}
잘못된 처리:
// ❌ 잘못됨: FAIL에 대해 method와 age가 항상 존재한다고 가정
const handleVerification = (result: VerificationResult) => {
if (result.data.status === "FAIL") {
// failureReason이 max-attempts-exceeded일 때 이 필드들은 undefined
logFailedMethod(result.data.method);
recordFailedAge(result.data.age.low);
showRetryWithMethod(result.data.method);
}
};
올바른 처리:
// ✅ 올바름: 다른 실패 시나리오를 적절히 처리
const handleVerification = (result: VerificationResult) => {
if (result.data.status === "FAIL") {
denyAccess();
switch (result.data.failureReason) {
case "max-attempts-exceeded":
// 연령 결정이 이루어지지 않음 - 대안 옵션 제공
showMaxAttemptsMessage();
offerSupportContact();
// 향후 시도에 대한 속도 제한 구현 고려
break;
case "age-criteria-not-met":
// 연령이 결정되었지만 기준을 충족하지 않음
// method 및 age 필드 사용 가능
if (result.data.age) {
logDeterminedAge(result.data.age.low);
}
showAgeCriteriaNotMetMessage();
break;
case "fraudulent-activity-detected":
// 의심스러운 활동 처리 (다음 섹션 참조)
handleSuspiciousActivity(result.data.id);
break;
default:
// 알 수 없는 실패 이유를 우아하게 처리
logUnknownFailure(result.data.failureReason);
showGenericFailureMessage();
}
}
};
의심스러운 활동 감지
시스템이 잠재적으로 사기 행위를 감지하면 확인은 fraudulent-activity-detected로 실패합니다. 이 응답은 보안상의 이유로 연령 데이터를 제외하고 method 필드를 포함하지 않습니다.
예제 페이로드:
{
"eventType": "Verification.Result",
"data": {
"id": "123e4567-e89b-12d3-a456-426614174003",
"status": "FAIL",
"failureReason": "fraudulent-activity-detected"
}
}
잘못된 처리:
// ❌ 잘못됨: 사기 활동을 일반 실패처럼 취급
const handleVerification = (result: VerificationResult) => {
if (result.data.status === "FAIL") {
// 즉시 재시도를 허용하면 지속적인 남용이 가능
showRetryButton();
// 존재하지 않는 연령 데이터 로깅
analytics.track("verification_failed", {
age: result.data.age?.low // 사기 활동의 경우 undefined
});
}
};
올바른 처리:
// ✅ 올바름: 적절한 보안 조치 구현
const handleVerification = (result: VerificationResult) => {
if (result.data.status === "FAIL") {
denyAccess();
if (result.data.failureReason === "fraudulent-activity-detected") {
// 검토를 위해 보안 이벤트 로깅
securityLog.warn("Fraudulent activity detected", {
verificationId: result.data.id,
timestamp: new Date().toISOString(),
subjectId: currentSubjectId
});
// 더 엄격한 속도 제한 또는 임시 차단 구현
applySecurityCooldown(currentSubjectId);
// 감지 세부 정보를 노출하지 않고 적절한 메시지 표시
showVerificationUnavailableMessage();
// 즉시 재시도를 제공하지 않음 - 지속적인 남용을 가능하게 함
hideRetryOptions();
// 선택적으로 수동 검토를 위해 플래그 지정
flagForManualReview(result.data.id);
}
}
};
완전한 엣지 케이스 핸들러
다음 예제는 모든 엣지 케이스를 올바르게 처리하는 포괄적인 핸들러를 보여줍니다:
interface VerificationData {
id: string;
status: "PASS" | "FAIL";
method?: string;
ageCategory?: "adult" | "digital-youth" | "digital-minor"; // PASS 상태일 때 항상 존재
age?: { low: number; high: number }; // PASS 상태일 때 항상 존재
dob?: string;
failureReason?: string;
}
const handleVerificationResult = (data: VerificationData) => {
// 항상 확인 시도 로깅
logVerificationAttempt(data.id, data.status);
if (data.status === "PASS") {
// 접근 권한 부여 - 사용자가 연령 기준을 충족함
grantAccess();
// age와 ageCategory는 PASS 상태일 때 항상 존재
storeAgeData(data.age.low, data.age.high);
applyPermissionsForCategory(data.ageCategory);
// 존재하는 경우에만 선택적 필드 처리
if (data.dob) {
storeDateOfBirth(data.dob);
}
if (data.method) {
analytics.track("verification_passed", { method: data.method });
}
return;
}
// FAIL 상태 처리
denyAccess();
// failureReason은 FAIL 상태에서 항상 존재
switch (data.failureReason) {
case "age-criteria-not-met":
// 이 실패 이유에서는 method와 age 사용 가능
handleAgeCriteriaFailure(data);
break;
case "max-attempts-exceeded":
// 연령 결정 없음 - method와 age 사용 불가
handleMaxAttemptsFailure(data.id);
break;
case "fraudulent-activity-detected":
// 보안 이벤트 - method와 age 사용 불가
handleFraudulentActivity(data.id);
break;
default:
// 알 수 없는 실패 이유는 항상 우아하게 처리
handleUnknownFailure(data);
}
};
선택적 필드에 접근하기 전에 항상 필드 존재 여부를 확인하세요. 확인 이벤트 계약은 각 상태 및 실패 이유 조합에 대한 완전한 필드 존재 규칙을 제공합니다.
확인 시도 제한
각 확인 요청은 사용 가능한 확인 방법당 사용자에게 세 번의 시도를 허용합니다. 다음 경우 확인이 실패합니다:
- 사용 가능한 모든 확인 방법이 소진되고 연령을 결정할 수 없는 경우
- 연령이 결정되지만 기준에 필요한 임계값보다 낮은 경우
확인이 실패하면 사용자가 새로운 확인 시도를 시작하도록 허용할 수 있습니다. 그러나 확인 시스템의 오용 및 남용을 방지하기 위해 추가 확인 시도에 대한 속도 제한을 구현해야 합니다. 예를 들어 24시간 내에 사용자를 세 번의 확인 시도로 제한할 수 있습니다.
여러 확인 요청에 걸쳐 시도를 추적하려면 확인 요청의 subject.id 필드를 사용하세요. 이 필드에는 사용자에 대한 일관된 식별자(예: 임시 세션 ID 또는 해시된 사용자 ID)가 포함되어야 하며, 이를 통해 다음을 수행할 수 있습니다:
- 사용자당 확인 시도 횟수 추적
- 시간 기반 속도 제한 구현(예: 24시간당 3회 시도)
- 새 세션을 만들어 제한을 우회하는 사용자 방지
확인 요청을 시작하기 전에 서버에서 속도 제한을 구현하세요. 이를 통해 불필요한 API 호출을 방지하고 시스템을 남용으로부터 보호하는 데 도움이 됩니다.
확인 방법
사용 가능한 모든 확인 방법에 대한 자세한 내용은 확인 방법을 참조하세요.