メインコンテンツに移動

モバイルアプリ

このガイドでは、k-IDの年齢確認をモバイルアプリケーションに統合するためのベストプラクティスについて説明します。Webでは、k-IDのインターフェースは通常iframeに埋め込まれますが、モバイルアプリでは確認URLを効果的に表示するために異なるアプローチが必要です。

概要

モバイルでサポートされるWeb埋め込みは、管轄区域を意識した確認インターフェース(AgeKeys、顔年齢推定、ID確認、その他の方法)を提示するホスト型URLです。2種類がサポートされています:

  • 年齢確認URL: /age-verification/perform-access-age-verificationエンドポイントから返されるurl(AgeKit+スタンドアロン確認)
  • 年齢保証チャレンジURL: /age-gate/checkから返されるCHALLENGE_AGE_GATE_AGE_ASSURANCEチャレンジのchallenge.url(自動年齢保証が有効な場合)、または/session/upgradeから返されるCHALLENGE_SESSION_UPGRADE_BY_AGE_ASSURANCEチャレンジのchallenge.url

このガイドの例では/age-verification/perform-access-age-verificationエンドポイントを使用しますが、同じ表示方法と結果処理はこれらのURLのいずれにも適用されます。

これらのURLの1つをモバイルアプリケーションに埋め込む場合、表示するためのいくつかのオプションがあり、それぞれ異なる機能とトレードオフがあります。主な考慮事項は次のとおりです:

  • AgeKeysサポート: ユーザーが将来の確認のためにAgeKeys(FIDOベースのパスキー)を作成して使用できるかどうか
  • 結果の通信: 確認結果がアプリにどのように配信されるか
  • ユーザーエクスペリエンス: 統合のレベルとネイティブ感
  • デバイスの向き: 確認はポートレート(縦向き)で最も効果的です。アプリ内ブラウザサーフェスはアプリの向きを継承するため、アプリが横向きにロックされている場合は、代わりにデフォルトの外部ブラウザを使用してユーザーがポートレートに回転できるようにします。デバイスの向きを参照してください
年齢ゲートとエンドツーエンドウィジェットはモバイルでは推奨されません

年齢確認URLと年齢保証チャレンジURLが、モバイルでサポートされる唯一のWeb埋め込みです。年齢ゲートウィジェットとエンドツーエンドウィジェットは、モバイルアプリには推奨されません。代わりに、カスタムワークフローを使用し、CDK UXガイドラインに従って年齢ゲートと同意のUX要素をネイティブに構築してください。

モバイル実装方法

Androidオプション

Androidは、確認URLを表示するための3つの主要な方法を提供します:

  • Custom Tabs推奨 - アプリのブランディングを維持するカスタマイズされたChromeブラウザタブで確認URLを開きます。これにより、ユーザーをアプリのコンテキスト内に保ちながら、完全なブラウザ機能を提供します。Custom TabsはChromeとCookieと認証状態を共有し、シームレスなエクスペリエンスを可能にします。

  • WebView - AndroidのネイティブWebViewコンポーネントを使用して、アプリ内にWebコンテンツを直接埋め込みます。実装は簡単ですが、WebViewは最新のWeb標準へのサポートが限られており、特定のブラウザ機能にアクセスできません。

推奨されません

WebViewはAgeKeysに必要なWebAuthnをサポートしていません。WebViewを使用して確認URLを埋め込む場合、ユーザーはAgeKeysを作成または使用できません。完全なAgeKeysサポートを備えた最適なユーザーエクスペリエンスを得るには、代わりにCustom Tabsを使用してください。

  • Trusted Web Activity (TWA) - 主にProgressive Web Apps向けに設計されたフルスクリーンモードでWebコンテンツを表示します。TWAは、アプリとk-IDドメイン間でデジタルアセットリンクを確立する必要があります。デジタルアセットリンクが検出されない場合、TWAは自動的にCustom Tabsにフォールバックします。
推奨されません

Trusted Web Activitiesは確認URLの埋め込みではサポートされていません。k-IDのドメインでデジタルアセットリンクを設定する必要がありますが、これは利用できません。この場合、TWAはCustom Tabsにフォールバックするため、より簡単な実装のためにCustom Tabsを直接使用することをお勧めします。

iOSオプション

iOSは、確認URLを表示するための3つの主要な方法を提供します:

  • ASWebAuthenticationSession推奨 - 安全な認証フロー専用に設計されたこの方法は、システム管理のブラウザビューでWebコンテンツを表示します。SafariとCookieを共有し、WebAuthnなどの最新のWeb機能へのアクセスを提供するため、確認フローに最適です。

  • SFSafariViewController - SafariとCookieと認証状態を共有するSafariライクなインターフェースでWebコンテンツを表示します。これにより、アプリのコンテキストを維持しながら、馴染みのあるブラウジングエクスペリエンスを提供します。

  • WKWebView - アプリ内にWebコンテンツを埋め込むAppleの最新のWebビューコンポーネントです。AndroidのWebViewと同様に、WKWebViewは特定のWeb標準に制限があり、すべてのブラウザ機能にアクセスできません。

推奨されません

WKWebViewはAgeKeysに必要なWebAuthnをサポートしていません。WKWebViewを使用して確認URLを埋め込む場合、ユーザーはAgeKeysを作成または使用できません。完全なAgeKeysサポートを備えた最適なユーザーエクスペリエンスを得るには、代わりにASWebAuthenticationSessionを使用してください。

デフォルトブラウザ(AndroidとiOS)

デフォルトの外部ブラウザは、AndroidとiOSの両方で機能します。確認URLをアプリ内ブラウザサーフェスで表示する代わりに、ユーザーのデフォルトブラウザを別のアプリとして起動します(AndroidではIntent.ACTION_VIEW、iOSではUIApplication.open)。

  • 完全なAgeKeysサポート - ブラウザはWebAuthnを提供するため、ユーザーはAgeKeysを作成して使用できます。
  • 独立した向き - ブラウザは別のアプリであるため、独自の向きを管理します。これにより、ユーザーが最適な確認エクスペリエンスのためにポートレートに回転できるため、横向きにロックされたアプリに推奨される方法となります。デバイスの向きを参照してください。
  • コールバックURLが必要 - ユーザーはアプリを離れるため、結果はredirectUrlコールバックを通じて配信され、フロー完了時にアプリにフォーカスを戻します。DOMメッセージは利用できません。

AgeKeysサポートの制限

AgeKeysは、FIDOおよびWebAuthn標準に基づく再利用可能な匿名の年齢証明資格情報です。ユーザーは一度年齢を確認し、個人情報を開示することなく、異なるサービス間でその確認を再利用できます。

AgeKeysの制限

AgeKeysにはWebAuthnサポートが必要ですが、これはAndroid WebViewまたはiOS WKWebViewでは利用できません。これらのコンポーネントを使用して確認URLを埋め込む場合、ユーザーは確認中にAgeKeysをオプションとして表示されず、確認成功後にAgeKeysを作成できません。

ユーザーに対してAgeKeysを有効にするには、次のいずれかの方法を使用する必要があります:

デバイスの向き

年齢確認はポートレート(縦向き)で最も効果的に機能します。顔年齢推定やID書類の撮影などの方法は、デバイスが縦向きのときに完了しやすく、確認インターフェースはポートレート向けにレイアウトされています。

アプリ内ブラウザサーフェス(Custom TabsASWebAuthenticationSessionSFSafariViewController、およびWebView/WKWebView)は、アプリの向きの制約を継承します。ゲームやアプリが横向きにロックされている場合、確認インターフェースも横向きに強制され、エクスペリエンスが低下します。

横向きロックされたアプリへの推奨事項

アプリが横向きにロックされている場合は、アプリ内ブラウザサーフェスではなく、デバイスのデフォルトの外部ブラウザで確認URLを開いてください。外部ブラウザは別のアプリとして実行され、独自の向きを管理するため、ユーザーは最適なエクスペリエンスのためにデバイスをポートレートに回転できます。

確認URLを生成する際にredirectUrlコールバックを設定してください。ユーザーが確認フローを完了すると、ブラウザはディープリンクにリダイレクトし、アプリまたはゲームにフォーカスを戻します。

外部ブラウザはAgeKeysを完全にサポートしています(WebAuthnが利用可能です)。Custom TabsやASWebAuthenticationSessionと同様に、DOMメッセージは利用できないため、結果を受信するにはコールバックURLを使用する必要があります。

デフォルトブラウザで確認URLを開くには、次のようにします:

import UIKit

func displayVerificationInBrowser(verificationUrl: URL) {
// システムのデフォルトブラウザ(別のアプリ)を開きます。これは
// アプリのロックされた向きに関係なく、独自の向きを管理します。
UIApplication.shared.open(verificationUrl)
}

戻ってくるディープリンクは、ステップ4に示すとおりに処理してください。

確認結果の受信

モバイルアプリは、ユーザーが確認フローを完了した後に結果を受信する必要があります。2つのアプローチがあり、それぞれ異なる可用性があります:

フィールドの存在ルール、ステータスタイプ、実装ガイダンスなど、確認結果の分析に関する詳細については、確認イベント契約を参照してください。

コールバックURL(ユニバーサル方法)

推奨アプローチ

すべての実装方法で推奨されるアプローチは、コールバックURLを使用することです。確認URLを生成するためにAPIを呼び出す際に、redirectUrlパラメータを含めます。確認フローが完了すると、結果をクエリパラメータとして含めてこのURLにリダイレクトします。

コールバックURLの利点:

  • すべての実装方法で動作
  • DOMメッセージよりも信頼性が高い
  • モバイルアプリの標準的なディープリンクパターン
  • アプリがバックグラウンドに移動しても、結果は常に配信されます

コールバックURLの仕組み

  1. アプリにディープリンクハンドラーを登録します(例:myapp://verification-complete
  2. APIを呼び出す際に、ディープリンクをredirectUrlとして含めます
  3. 確認ページは完了後にディープリンクにリダイレクトします
  4. アプリがディープリンクを処理し、結果を抽出します
備考

リダイレクトは、確認URLがブラウザまたはWebビューで直接開かれた場合にのみ発生し、iframeに埋め込まれた場合は発生しません。

コールバックURLパラメータ

確認ページがコールバックURLにリダイレクトする際、フローに関連するクエリパラメータが含まれます。例えば:

  • 年齢確認にはverificationIdresultが含まれます
  • URLがセッションアップグレードに由来する場合、sessionIdとステータス情報も含まれる場合があります

コールバックURLの例:

myapp://verification-complete?verificationId=7854909b-9124-4bed-9282-24b44c4a3c97&result=PASS

コールバックURLの実装

年齢確認APIを呼び出す際に、リクエストにredirectUrlを含めます。URLは次のいずれかになります:

  • HTTPS URL: https://example.com/verification-complete
  • カスタムディープリンク: myapp://verification-complete

DOMメッセージ(WebView/WKWebViewのみ)

Android WebViewまたはiOS WKWebViewを使用する場合、確認ページから送信されるJavaScriptメッセージをリッスンできます。これにより、以下が可能になります:

  • 確認結果をリアルタイムで受信
  • Webビューを閉じるタイミングを制御
  • 確認イベントに基づいてアプリのUIを更新

年齢確認インターフェースはVerification.Resultイベントを発行します。このイベントにはstatusPASSFAILなど)と、成功時には解決されたageCategoryが含まれます。

DOMメッセージは、ネイティブコードでインターセプトできるpostMessageイベントとして送信されます。利用可能なイベントの詳細については、DOMイベントの概要を参照してください。

限定的な可用性

DOMメッセージはWebViewとWKWebViewでのみ機能し、これらはAgeKeysをサポートしていません。Custom TabsTrusted Web ActivityASWebAuthenticationSession、またはSFSafariViewControllerでは利用できません。これらのコンポーネントはAgeKeysを作成できないため、代わりに推奨される表示方法とコールバックURLを使用してください。

プラットフォーム固有の実装

iOS WKWebViewの場合、kidという名前のメッセージハンドラーを登録することで、k-IDイベントをネイティブに受信できます。確認ページはこのハンドラーを自動的に検出し、イベントを直接送信します。ただし、Android WebViewにはpostMessageイベントを受信するネイティブメカニズムがありません。JavaScriptを注入してメッセージをリッスンし、JavaScriptインターフェースを介してネイティブコードに転送する必要があります。

サードパーティアプリ認証フロー

ConnectIDなどの一部の認証方法では、認証プロセスの一環としてユーザーをサードパーティのモバイルアプリにリダイレクトする必要があります。例えば、ConnectIDはユーザーの銀行アプリを開いて本人確認を完了します。

サードパーティアプリフローの仕組み

サードパーティアプリを使用する認証方法を使用する場合、フローは複数のアプリケーションを経由してからアプリに戻ります:

ステップごとの説明:

  1. アプリがサーバーに認証URLをリクエストします
  2. サーバーがAPIキーを使用してk-ID APIを呼び出し、アプリのredirectUrlを含めます
  3. k-IDが認証インターフェースを含むURLをサーバーに返します
  4. サーバーがアプリにURLを返します
  5. アプリがWebコンポーネント(ASWebAuthenticationSessionまたはCustom Tabs)でURLを開きます
  6. ユーザーがConnectIDなどの認証方法を選択すると、k-ID UIがサードパーティの認証アプリ(銀行アプリなど)にディープリンクします
  7. 認証が完了すると、サードパーティアプリはデバイスのネイティブブラウザでk-IDの結果ページにリダイレクトします
  8. k-IDが保存されたredirectUrlを取得し、認証結果とともにユーザーをアプリにリダイレクトします

サードパーティアプリフローの重要な考慮事項

Webコンポーネントの要件: これらの認証方法は、埋め込みWebViewではなく、システムブラウザコンテキスト(ASWebAuthenticationSession、Custom Tabs、またはSFSafariViewController)で開く必要があります。サードパーティアプリのリダイレクトフローが正しく機能するには、完全なブラウザコンテキストが必要です。

ネイティブブラウザへのハンドオフ: サードパーティアプリが認証を完了すると、元のWebコンポーネントに直接戻るのではなく、デバイスのネイティブブラウザでk-ID URLにリダイレクトされます。これは、モバイルデバイスでのアプリ間リダイレクトの動作によるプラットフォームの制限です。

コールバックURLは必須: 認証フローが複数のアプリとブラウザを経由するため、redirectUrlパラメータは完了後にユーザーをアプリに戻すために重要です。サードパーティアプリ方法を使用する可能性のある認証を開始する際は、常にredirectUrlを含めてください。

サードパーティアプリフローのテスト

以下の認証方法はサードパーティアプリのリダイレクトを使用します:

  • ConnectID: モバイルアプリケーションでのリダイレクトフローを検証するためのテストアプリが含まれています。

方法の比較

方法プラットフォームAgeKeysDOMメッセージコールバックURL最適な用途
WebViewAndroid推奨されません(AgeKeysサポートなし)
Custom TabsAndroidほとんどのユースケース(推奨)
Trusted Web ActivityAndroid推奨されません(デジタルアセットリンクが必要)
WKWebViewiOS推奨されません(AgeKeysサポートなし)
ASWebAuthenticationSessioniOSほとんどのユースケース(推奨)
SFSafariViewControlleriOSSafariライクなエクスペリエンス
デフォルトの外部ブラウザAndroid & iOS横向きロックされたアプリ(ポートレート回転を許可)

推奨実装

Android: Custom Tabs

機能とユーザーエクスペリエンスの最適なバランスを得るために、コールバックURLと共にCustom Tabsを使用します。

Custom Tabsを選ぶ理由:

  • WebAuthnによる完全なAgeKeysサポート
  • すべての最新Web機能へのアクセス
  • アプリブランディングによるシームレスなユーザーエクスペリエンス
  • 信頼性の高いコールバックメカニズム
  • Chromeと認証状態を共有

実装手順:

  1. コールバックURLのディープリンクハンドラーを登録します
  2. APIリクエストにredirectUrlを含めます
  3. Custom Tabsを使用して確認URLを開きます
  4. 確認結果でディープリンクコールバックを処理します

iOS: ASWebAuthenticationSession

安全でネイティブ感のあるコンプライアンスフローのために、コールバックURLと共にASWebAuthenticationSessionを使用します。

ASWebAuthenticationSessionを選ぶ理由:

  • WebAuthnによる完全なAgeKeysサポート
  • すべての最新Web機能へのアクセス
  • システム管理のセキュリティUI
  • SafariとCookieを共有
  • 信頼性の高いコールバックメカニズム

実装手順:

  1. コールバックURLのURLスキームハンドラーを登録します
  2. APIリクエストにredirectUrlを含めます
  3. ASWebAuthenticationSessionを使用して確認URLを表示します
  4. 確認結果でURLスキームコールバックを処理します

完全な実装例

推奨アプローチを実装するためのステップバイステップの例と完全なコードサンプルを以下に示します:

ステップ1: ディープリンクハンドラーの登録

Info.plistにURLスキームを登録します:

<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>myapp</string>
</array>
</dict>
</array>

ステップ2: サーバーから確認URLを生成

重要

確認URLは、モバイルアプリから直接ではなく、サーバーから生成する必要があります。これにより、APIキーがクライアント側のコードに公開されるのを防ぎます。モバイルアプリは独自のサーバーAPIを呼び出し、サーバーがk-IDへのサーバー間呼び出しを行います。

アーキテクチャの概要

サーバー実装

サーバーが/age-verification/perform-access-age-verificationエンドポイントをAPIキーで呼び出し、redirectUrlディープリンクを含めます:

POST https://game-api.k-id.com/api/v1/age-verification/perform-access-age-verification
Content-Type: application/json
Authorization: Bearer YOUR_API_KEY

{
"jurisdiction": "US-CA",
"criteria": {
"ageCategory": "DIGITAL_YOUTH_OR_ADULT"
},
"options": {
"redirectUrl": "myapp://verification-complete"
}
}
備考

テストには、テスト環境のエンドポイントを使用してください:https://game-api.test.k-id.com/api/v1/age-verification/perform-access-age-verification

レスポンス:

{
"id": "7854909b-9124-4bed-9282-24b44c4a3c97",
"url": "https://family.k-id.com/verify?token=eyJhbGciOiJFUzM4NCIs...",
"shortUrl": "https://family.k-id.com/v/7854909b-9124-4bed-9282-24b44c4a3c97?pid=42&s=qr"
}
その他のサポートされているモバイルURL

同じ表示と結果処理は、年齢保証チャレンジURLにも適用されます:/age-gate/checkからのCHALLENGE_AGE_GATE_AGE_ASSURANCEチャレンジのchallenge.url(自動年齢保証が有効な場合)、および/session/upgradeからのCHALLENGE_SESSION_UPGRADE_BY_AGE_ASSURANCEチャレンジのchallenge.urlです。URLを生成するエンドポイントが異なるだけです。

モバイルクライアント実装

モバイルアプリがサーバーを呼び出して確認URLを取得します:

import Foundation

func fetchVerificationUrl(completion: @escaping (URL?) -> Void) {
// k-IDではなく、自分のサーバーエンドポイントを呼び出す
guard let url = URL(string: "https://your-server.com/api/generate-verification-url") else {
completion(nil)
return
}

var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
// 独自の認証を追加(セッショントークンなど)
request.setValue("Bearer USER_SESSION_TOKEN", forHTTPHeaderField: "Authorization")

let requestBody: [String: Any] = [
"jurisdiction": "US-CA",
"criteria": [
"ageCategory": "DIGITAL_YOUTH_OR_ADULT"
],
"options": [
"redirectUrl": "myapp://verification-complete"
]
]

guard let httpBody = try? JSONSerialization.data(withJSONObject: requestBody) else {
completion(nil)
return
}
request.httpBody = httpBody

URLSession.shared.dataTask(with: request) { data, response, error in
guard error == nil,
let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode),
let data = data,
let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
let verificationUrlString = json["url"] as? String,
let verificationUrl = URL(string: verificationUrlString) else {
completion(nil)
return
}
completion(verificationUrl)
}.resume()
}

ステップ3: 確認URLの表示

import AuthenticationServices

// セッションをプロパティとして保存して、解放を防ぐ
var authSession: ASWebAuthenticationSession?

func displayVerification(verificationUrl: URL) {
authSession = ASWebAuthenticationSession(
url: verificationUrl,
callbackURLScheme: "myapp"
) { callbackURL, error in
if let error = error {
// エラーを処理(ユーザーがキャンセルした場合など)
return
}
if let callbackURL = callbackURL {
handleVerificationCallback(callbackURL)
}
}
authSession?.presentationContextProvider = self
authSession?.start()
}

ステップ4: コールバックの処理

func handleVerificationCallback(_ callbackURL: URL) {
// これが期待しているコールバックURLかどうかを確認
guard callbackURL.scheme == "myapp",
callbackURL.host == "verification-complete",
let components = URLComponents(url: callbackURL, resolvingAgainstBaseURL: false),
let queryItems = components.queryItems else {
return
}

let verificationId = queryItems.first(where: { $0.name == "verificationId" })?.value
let result = queryItems.first(where: { $0.name == "result" })?.value
// 確認URLがセッションアップグレードに由来する場合のみ存在
let sessionId = queryItems.first(where: { $0.name == "sessionId" })?.value

// 確認結果に基づいてUIを更新
if result == "PASS" {
// 成功した確認を処理
} else if result == "FAIL" {
// 失敗した確認を処理
}

// オプション:APIエンドポイントを使用してサーバー側で確認
if let verificationId = verificationId {
verifyResultServerSide(verificationId: verificationId)
} else if let sessionId = sessionId {
verifyResultServerSide(sessionId: sessionId)
}
}
ベストプラクティス

セキュリティとデータの整合性のために、クライアント側のデータのみに依存するのではなく、適切なAPIエンドポイントを使用してサーバー側で結果を常に確認してください。確認には/age-verification/get-statusエンドポイントを使用し、確認URLがセッションアップグレードに由来する場合は/session/getを使用してください。フィールドの存在ルール、ステータスタイプ、実装ガイダンスなど、確認結果の分析に関する詳細については、確認イベント契約を参照してください。webhookを使用している場合は、リトライポリシーと受信できなかったイベントの回復方法について配信、リトライ、リカバリーを参照してください。

サポートされているモバイルWeb埋め込み

モバイルでは、これらのホスト型URLのみを埋め込んでください。すべて同じ方法で表示および処理されます:

年齢ゲートと同意フロー自体については、年齢ゲートウィジェットとエンドツーエンドウィジェットがモバイルでは推奨されないため、カスタムワークフローでUXをネイティブに構築し、CDK UXガイドラインに従ってください。