Skip to main content

Mobile implementation

This guide covers best practices for integrating CDK widgets into mobile applications. On the web, widgets are commonly embedded in iframes, but mobile apps need different approaches to display widget URLs effectively.

Overview

CDK widgets are URLs returned from API calls that contain complete compliance flows, including age gates, VPC, data notices, and permission management. When integrating these widgets into mobile applications, you have several options for displaying them, each with different capabilities and trade-offs. The key considerations are:

  • AgeKeys support: Whether users can create and use AgeKeys (FIDO-based passkeys) for future verifications
  • Result communication: How widget results are delivered back to your app
  • User experience: The level of integration and native feel

Mobile implementation methods

Android options

Android provides three primary methods for displaying widget URLs:

  • Chrome Custom TabsRecommended - Opens the widget URL in a customized Chrome browser tab that maintains your app's branding. This provides full browser capabilities while keeping users within your app's context. Custom Tabs share cookies and authentication state with Chrome, enabling seamless experiences.

  • WebView - Embeds web content directly within your app using Android's native WebView component. While simple to implement, WebView has limited support for modern web standards and can't access certain browser features.

Not recommended

WebView doesn't support WebAuthn, which is required for AgeKeys. Users won't be able to create or use AgeKeys when widgets are embedded using WebView. For the best user experience with full AgeKeys support, use Chrome Custom Tabs instead.

  • Trusted Web Activity (TWA) - Displays web content in full-screen mode, primarily designed for Progressive Web Apps. TWAs require establishing a digital asset link between your app and the k-ID domain. When digital asset links aren't detected, TWA automatically falls back to Chrome Custom Tabs.
Not recommended

Trusted Web Activities aren't supported for widget integration. Digital asset links would need to be configured on k-ID's domains, which isn't available. Since TWA falls back to Chrome Custom Tabs in this case, use Chrome Custom Tabs directly for a simpler implementation.

iOS options

iOS provides three primary methods for displaying widget URLs:

  • ASWebAuthenticationSessionRecommended - Designed specifically for secure authentication flows, this method presents web content in a system-managed browser view. It shares cookies with Safari and provides access to modern web features such as WebAuthn, making it ideal for verification flows.

  • SFSafariViewController - Presents web content in a Safari-like interface that shares cookies and authentication state with Safari. This provides a familiar browsing experience while maintaining app context.

  • WKWebView - Apple's modern web view component that embeds web content within your app. Similar to Android's WebView, WKWebView has limitations with certain web standards and can't access all browser features.

Not recommended

WKWebView doesn't support WebAuthn, which is required for AgeKeys. Users won't be able to create or use AgeKeys when widgets are embedded using WKWebView. For the best user experience with full AgeKeys support, use ASWebAuthenticationSession instead.

AgeKeys support limitations

AgeKeys are reusable, anonymous age-proof credentials based on FIDO and WebAuthn standards. They allow users to verify their age once and reuse that verification across different services without revealing personal information.

AgeKeys limitation

AgeKeys require WebAuthn support, which isn't available in Android WebView or iOS WKWebView. If you embed widgets by using these components, users won't see AgeKeys as an option during verification, and they can't create AgeKeys after successful verification.

To enable AgeKeys for your users, you must use one of these methods:

Receiving widget results

Your mobile app needs to receive results after users complete the widget flow. There are two approaches, each with different availability:

Callback URL (universal method)

Recommended approach

The recommended approach for all implementation methods is to use a callback URL. When you call the API to generate a widget URL, include a redirectUrl parameter. After the widget flow completes, it redirects to this URL with the results included as query parameters.

Advantages of callback URLs:

  • Works with all implementation methods
  • More reliable than DOM messages
  • Standard deep linking pattern for mobile apps
  • Results are always delivered, even if the app moves to the background

How callback URLs work

  1. Register a deep link handler in your app (for example, myapp://vpc-complete)
  2. Include the deep link as redirectUrl when calling the API
  3. The widget redirects to your deep link after completion
  4. Your app handles the deep link and extracts the results
note

Redirects only occur when the widget URL is opened directly in a browser or web view, not when embedded in an iframe.

Callback URL parameters

When the widget redirects to your callback URL, it includes query parameters relevant to the widget type. For example:

  • Age gate widgets can include verificationId and result
  • Session-related widgets can include sessionId and status information

Example callback URL:

myapp://vpc-complete?sessionId=608616da-4fd2-4742-82bf-ec1d4ffd8187&result=PASS

Implementing callback URLs

Include redirectUrl in your request when calling CDK widget APIs. The URL can be:

  • An HTTPS URL: https://example.com/vpc-complete
  • A custom deep link: myapp://vpc-complete
note

Not all CDK widget endpoints support redirectUrl yet. Check the specific API endpoint documentation for availability. When redirectUrl isn't available, use DOM messages with WebView or WKWebView.

DOM messages (WebView/WKWebView only)

When using Android WebView or iOS WKWebView, you can listen for JavaScript messages sent from the widget. This allows you to:

  • Receive widget results in real-time
  • Control when the web view closes
  • Update your app's UI based on widget events

CDK widgets emit different DOM events depending on the widget type:

DOM messages are sent as postMessage events that you can intercept in your native code. For details about available events, see the DOM events overview.

Limited availability

DOM messages only work with WebView and WKWebView. They're not available with Chrome Custom Tabs, Trusted Web Activity, ASWebAuthenticationSession, or SFSafariViewController.

Method comparison

MethodPlatformAgeKeysDOM MessagesCallback URLBest For
WebViewAndroidNot recommended (no AgeKeys support)
Chrome Custom TabsAndroidMost use cases (recommended)
Trusted Web ActivityAndroidNot recommended (requires digital asset links)
WKWebViewiOSNot recommended (no AgeKeys support)
ASWebAuthenticationSessioniOSMost use cases (recommended)
SFSafariViewControlleriOSSafari-like experience

Android: Chrome Custom Tabs

Use Chrome Custom Tabs with callback URLs for the best balance of features and user experience.

Why Chrome Custom Tabs:

  • Full AgeKeys support via WebAuthn
  • Access to all modern web features
  • Seamless user experience with app branding
  • Reliable callback mechanism
  • Shares authentication state with Chrome

Implementation steps:

  1. Register a deep link handler for your callback URL
  2. Include redirectUrl in your API request (if supported by the endpoint)
  3. Open the widget URL using Chrome Custom Tabs
  4. Handle the deep link callback with widget results

iOS: ASWebAuthenticationSession

Use ASWebAuthenticationSession with callback URLs for secure, native-feeling compliance flows.

Why ASWebAuthenticationSession:

  • Full AgeKeys support via WebAuthn
  • Access to all modern web features
  • System-managed security UI
  • Shares cookies with Safari
  • Reliable callback mechanism

Implementation steps:

  1. Register a URL scheme handler for your callback URL
  2. Include redirectUrl in your API request (if supported by the endpoint)
  3. Present the widget URL using ASWebAuthenticationSession
  4. Handle the URL scheme callback with widget results

Complete implementation example

Here's a step-by-step example of implementing the recommended approach with complete code samples:

Register a URL scheme in Info.plist:

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

Step 2: Generate widget URL with callback

import Foundation

func generateWidgetUrl(completion: @escaping (URL?) -> Void) {
guard let url = URL(string: "https://game-api.k-id.com/api/v1/widget/generate-e2e-url") else {
completion(nil)
return
}
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("Bearer YOUR_API_KEY", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")

let requestBody: [String: Any] = [
"jurisdiction": "US-CA",
"options": [
"redirectUrl": "myapp://vpc-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
if let error = error {
completion(nil)
return
}

guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode),
let data = data,
let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
let widgetUrlString = json["url"] as? String,
let widgetUrl = URL(string: widgetUrlString) else {
completion(nil)
return
}
completion(widgetUrl)
}.resume()
}

Step 3: Display the widget

import AuthenticationServices

// Store session as a property to prevent deallocation
var authSession: ASWebAuthenticationSession?

func displayWidget(widgetUrl: URL) {
authSession = ASWebAuthenticationSession(
url: widgetUrl,
callbackURLScheme: "myapp"
) { callbackURL, error in
if let error = error {
// Handle error (user cancelled, etc.)
return
}
if let callbackURL = callbackURL {
handleWidgetCallback(callbackURL)
}
}
authSession?.presentationContextProvider = self
authSession?.start()
}

Step 4: Handle the callback

func handleWidgetCallback(_ callbackURL: URL) {
guard let components = URLComponents(url: callbackURL, resolvingAgainstBaseURL: false),
let queryItems = components.queryItems else {
return
}

let sessionId = queryItems.first(where: { $0.name == "sessionId" })?.value
let verificationId = queryItems.first(where: { $0.name == "verificationId" })?.value
let result = queryItems.first(where: { $0.name == "result" })?.value

// Update UI based on widget result
if result == "PASS" {
// Handle successful flow
} else if result == "FAIL" {
// Handle failed flow
}

// Optionally verify server-side using CDK API endpoints
if let sessionId = sessionId {
verifyResultServerSide(sessionId: sessionId)
}
}

CDK widget types

CDK provides several widget types, each designed for specific compliance workflows:

  • End-to-End widget: Handles the complete VPC flow in a single interface, including age gate, verification, data notices, permissions, and preferences
  • Age Gate widget: Collects user age and triggers VPC challenges when needed
  • Data Notices widget: Displays jurisdiction-appropriate data notices and collects user consent
  • Session Permissions widget: Allows users and parents to view and update active session permissions

For detailed information about each widget type and their specific use cases, see the guides in the CDK documentation.