メインコンテンツに移動

ウォーターフォールフロー

k-IDによる年齢確認は、ユーザーが個人情報を開示することなく年齢を証明できるプライバシー保護型のプロセスです。このアプローチはウォーターフォールフローモデルを使用します。

ウォーターフォールフロー

AgeKit+は年齢チェックの単一ポイントオーケストレーターとして機能し、ユーザーの年齢を確認するために確認プロバイダーのウォーターフォールを自動的にカスケードします。実際には、k-IDへの1つのAPI呼び出しで、設定された方法が順番に提示されます。たとえば、メール推論または顔年齢推定から始まり、必要に応じてID文書スキャンまたはその他の方法にフォールバックし、ユーザーの年齢が確認されるか、すべてのオプションが使い果たされるまで続きます。これは、開発者がk-IDのAPIと一度統合し、プラットフォームが背後で複数の確認技術を試行し、方法を組み合わせて確認の成功の可能性を最大化することを意味します。

確認フローは、iframeまたはモバイルWebビューでホストされる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がブラウザまたはWebビューで直接開かれた場合にのみ発生します(iframeに埋め込まれていない場合)。リダイレクトが発生すると、URLにはverificationIdresult(PASSまたはFAIL)がクエリ文字列パラメータとして含まれます。いいえ

passIfOverfailIfUnderパラメータは、顔年齢推定結果で許可される分散を制御できます。顔年齢推定スキャンが実行されると:

  • 推定年齢がpassIfOver以上の場合、確認はパスし、年齢シグナルが決定されます。
  • 推定年齢がfailIfUnder未満の場合、確認は失敗し、年齢シグナルが決定されます。
  • 推定年齢がfailIfUnderpassIfOverにある場合、結果は不確定と見なされ、ユーザーは顔年齢推定を再試行できます。

これにより、結果が明確に判断できる信頼範囲を設定しながら、推定が不確実な範囲に該当する場合にユーザーが再試行できるようになります。たとえば、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パラメータを使用すると、確認完了後にユーザーをリダイレクトする場所を指定できます。これは次の場合に役立ちます:

  • ブラウザベースのフロー: 確認完了後に別のWebページにリダイレクト
  • カスタム成功画面: 独自のカスタム成功または失敗ページを表示
  • モバイルアプリディープリンク: カスタムプロトコルスキーム(例:myapp://verification-complete)を使用してモバイルアプリに制御を戻す
important

リダイレクトは、確認URLがブラウザまたはWebビューで直接開かれた場合にのみ発生します(iframeに埋め込まれていない場合)。iframeに埋め込まれている場合、確認結果はDOMイベントを通じて配信されます。

リダイレクトが発生すると、リダイレクトURLには次のクエリ文字列パラメータが含まれます:

  • verificationId: 一意の確認ID
  • result: 確認結果、PASSまたはFAIL

リダイレクトURLの例:

https://example.com/verification-complete?verificationId=7854909b-9124-4bed-9282-24b44c4a3c97&result=PASS

レスポンス本文

年齢確認APIへの成功したリクエストは、以下のレスポンスを返します。

プロパティ説明
id年齢確認サービスによって生成された一意の確認ID
urliframeに埋め込み、ユーザーに提示して自分自身を確認してもらう必要がある年齢確認URL。

サンプル:

{
"id": "7854909b-9124-4bed-9282-24b44c4a3c97",
"url": "https://family.k-id.com/verify?token=eyJ..."
}

確認インターフェースの埋め込み

返されたURLを使用して、Webサイトまたはアプリに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要素を制御するのに最適ですが、データの整合性については、実際の結果はwebhookまたは/age-verification/get-statusへの呼び出しから取得する必要があります。

クライアント側(DOMイベント) - レスポンス本文のURLがiframeに含まれている場合、Verification.Result構造を持つウィンドウメッセージ(MessageEvent)として親フレームに送信されます。

サーバー側(webhooks) - イベントは、Verification.Resultイベントの形式で登録されたwebhookに送信されます。

ウィンドウメッセージにアクセスする例:

const handleMessage = (event: MessageEvent) => {
const message = event.data;
if (message.eventType === "Verification.Result") {
// DOMイベントを即座のUI更新に使用
updateUI(message);
}
};

window.addEventListener("message", handleMessage);
important

データの整合性については、DOMイベントのみに依存するのではなく、webhooksからのイベントまたは/age-verification/get-statusへの呼び出しで結果を常に確認してください。DOMイベントは応答性の高いUI更新に最適です。

ウィンドウイベントとwebhookイベントのデータ要素には、以下のプロパティが含まれます。

プロパティ説明
idこれが結果である確認ID。
statusユーザーが年齢基準を満たしたかどうかに基づいて、PASSまたはFAILステータスを示します。
ageCategoryリクエストで指定された管轄区域でユーザーが属する年齢カテゴリを示します。サポートされている値はadultdigital-youth、またはdigital-minorです。
method確認に使用された方法を示します。サポートされている値はid-documentage-estimationage-attestationcredit-cardsocial-security-numberです。
failureReason確認が失敗した理由。サポートされている値はage-criteria-not-metmax-attempts-exceeded、またはfraudulent-activity-detectedです。これはstatusFAILの場合にのみ設定されます
age推定または確認された年齢の下限と上限をlowhighとして返します。

サンプル:

{
"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 webhookの両方でイベントを送信することに加えて、確認のステータスをクエリすることも可能です。これは、登録されたwebhookが一定期間到達不能で、ステータスイベントが送信されなかった場合を処理するのに役立ちます。確認のステータスを取得するには、/age-verification/get-status APIを使用します。返されるデータ構造は、Verification.Result webhookイベントと同じ契約に従いますが、構造とフィールドの存在にいくつかの違いがあります。フィールドの存在ルール、ステータスタイプ、webhookとAPIエンドポイント応答の違い、実装ガイダンスなど、確認結果の分析に関する詳細については、確認イベント契約を参照してください。webhookイベントの詳細については、Webhooksを参照してください。

エッジケースの処理

確認結果を処理する際、確認結果と失敗理由に応じてレスポンス構造に変動があることを考慮する必要があります。以下の例は、異なるレスポンス変動に対する正しい処理パターンと誤った処理パターンを示しています。完全なフィールド存在ルールについては、確認イベント契約を参照してください。

部分的な試行(最大試行回数超過)

ユーザーが確定的な年齢決定なしにすべての確認試行を使い果たした場合、確認はmax-attempts-exceededで失敗します。このレスポンスにはmethodage、または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);
}
};
ベストプラクティス

オプションのフィールドにアクセスする前に、常にフィールドの存在を確認してください。確認イベント契約は、各ステータスと失敗理由の組み合わせに対する完全なフィールド存在ルールを提供します。

確認試行の制限

各確認リクエストでは、利用可能な確認方法ごとにユーザーに3回の試行が許可されます。確認は、以下の場合に失敗します:

  • 利用可能なすべての確認方法が使い果たされ、年齢を決定できない
  • 年齢が決定されたが、基準の必要な閾値を下回る

確認が失敗した場合、ユーザーに新しい確認試行を開始することを許可できます。ただし、確認システムの誤用や悪用を防ぐために、追加の確認試行にレート制限を実装する必要があります。たとえば、24時間以内に3回の確認試行にユーザーを制限する場合があります。

確認リクエストのsubject.idフィールドを使用して、複数の確認リクエストにわたる試行を追跡します。このフィールドには、ユーザーの一貫した識別子(一時セッションIDまたはハッシュ化されたユーザーIDなど)を含める必要があり、以下を可能にします:

  • ユーザーごとの確認試行回数を追跡する
  • 時間ベースのレート制限を実装する(たとえば、24時間あたり3回の試行)
  • ユーザーが新しいセッションを作成して制限を回避することを防ぐ
ベストプラクティス

確認リクエストを開始する前に、サーバーでレート制限を実装してください。これにより、不要なAPI呼び出しを防ぎ、システムを悪用から保護するのに役立ちます。

確認方法

利用可能なすべての確認方法の詳細については、確認方法を参照してください。