azakaw_kyc_flutter 1.0.0
azakaw_kyc_flutter: ^1.0.0 copied to clipboard
Azakaw KYC onboarding for Flutter — runs the Azakaw compliance onboarding flow in a WebView, with region and environment selection matching the Azakaw web SDK.
azakaw_kyc_flutter #
Runs the Azakaw KYC / compliance onboarding flow inside your Flutter app.
The flow itself is the Azakaw onboarding portal, hosted in a WebView. The
configuration surface mirrors the Azakaw web SDK
(@azakawcompliance/azakaw-web-sdk) — the same regions, the same
environments, and the same URL derivation — so a session behaves the same way
on web and on mobile.
Features #
- Region selection: UAE, KSA, Qatar, Kuwait
- Environment selection: Sandbox, Production
- A real completion signal — the portal's
OnboardingCompletedevent reaches your Dart code - Camera permission handling for document and liveness capture, on both Android and iOS
hideSidebar/hideOnboardingSections, delivered over the same handshake the web SDK uses- Arabic / RTL preselection
Install #
dependencies:
azakaw_kyc_flutter: ^1.0.0
Requires Flutter 3.24+, iOS 14.3+, Android API 21+.
The bundled
example/app needs Flutter 3.35+ to build for iOS, because iOS 26+ requires the UIScene lifecycle. That applies to running the example only, not to integrating the SDK.
iOS 14.3 is the floor because WKWebView gained getUserMedia there — document and liveness capture cannot work below it. On iOS 15+ the SDK handles the camera prompt itself; on 14.3–14.x WKWebView prompts using your NSCameraUsageDescription.
Usage #
import 'package:azakaw_kyc_flutter/azakaw_kyc_flutter.dart';
final result = await AzakawKyc.start(
context: context,
config: AzakawKycConfig(
sessionId: sessionId, // from your backend
region: AzakawRegion.uae,
environment: AzakawEnvironment.sandbox,
),
);
switch (result.status) {
case AzakawKycStatus.completed:
// Confirm the outcome server-side against the session id before
// treating the user as verified.
break;
case AzakawKycStatus.cancelled:
break;
case AzakawKycStatus.failed:
debugPrint('${result.error}');
break;
}
AzakawKyc.start also accepts onComplete, onCancel and onError
callbacks if you prefer them to the returned future.
To embed the flow in your own layout instead of a full-screen route, use
AzakawKycView directly.
Configuration #
| Field | Type | Default | Description |
|---|---|---|---|
sessionId |
String |
— | Required. Session id from the Azakaw backend. Must be non-empty. |
region |
AzakawRegion |
uae |
Selects the portal domain. |
environment |
AzakawEnvironment |
sandbox |
Selects the portal subdomain. |
hideSidebar |
bool |
false |
Hides the portal's sidebar. |
hideOnboardingSections |
bool |
false |
Starts on the first form page and hides the sections page until onboarding completes. |
language |
AzakawLanguage? |
null |
en or ar. ar also switches the portal to RTL. null leaves the portal's own default. |
isFullscreen |
bool |
true |
Full-screen route, or an inset dialog. |
resolveTenantHost |
bool |
true |
Opens the flow directly on the tenant's own domain. See below. |
enableLogging |
bool |
false |
Logs protocol traffic via debugPrint. Never enable in release — payloads include the session id. |
hideSidebar and hideOnboardingSections reach the portal over the
postMessage handshake rather than the URL, so they apply once the portal has
booted, not at load time. This matches the web SDK.
Regions and environments #
| Region | Domain | Sandbox | Production |
|---|---|---|---|
uae (default) |
azakaw.com |
sandbox.azakaw.com |
app.azakaw.com |
ksa |
azakawksa.com |
— not available | app.azakawksa.com |
qatar |
azakawqatar.com |
— not available | app.azakawqatar.com |
kuwait |
azakaw.com |
kuwait-stg.azakaw.com |
kuwait.azakaw.com |
Sandbox is provisioned for UAE and Kuwait. Pair
ksaandqatarwithAzakawEnvironment.production; those combinations compose a URL — as they do in the web SDK — but the host does not resolve.AzakawKycConfig.isUnavailableCombinationreports this, and the SDK logs a warning in debug builds.Kuwait does not follow the
app/sandboxpattern. It shares theazakaw.comdomain with UAE and is separated by subdomain instead, so its API and auth hosts arekuwait-api/kuwait-authandkuwait-stg-api/kuwait-stg-auth.AzakawRegion.subdomainForis the authority; do not assume the convention.
Tenant domains #
Tenants can run the portal on their own domain, often with their own theming.
The portal reports that domain only after it has booted and decoded the
session token (the TenantBaseUrl event), so the generic host renders first
and the user sees a flash of the wrong brand.
With resolveTenantHost (the default), the SDK reads the same baseUrl
claim from the session token up front — one anonymous
GET {authUrl}AppSessionManager/GetToken/{sessionId}, which is a pure read
and does not consume the session — and opens the WebView on the tenant
domain directly.
It is only an optimisation. If the lookup fails, times out, or the token
names no domain, the region host loads and the portal's TenantBaseUrl
redirect still corrects it, exactly as in the web SDK. Set
resolveTenantHost: false to skip the extra request and rely on the redirect
alone.
Permissions #
The plugin declares INTERNET and CAMERA in its own manifest, so Android
host apps inherit them through manifest merging — you do not need to add them
yourself. It also declares android.hardware.camera as not required, so
your Play Store device filtering is unaffected.
On iOS you must do two things.
- Add a camera usage description to
ios/Runner/Info.plist:
<key>NSCameraUsageDescription</key>
<string>Camera access is required for identity verification</string>
- Enable the camera permission in your
ios/Podfile.permission_handlercompiles every permission out by default (#ifndef PERMISSION_CAMERA→#define PERMISSION_CAMERA 0), so without this the camera request always reports denied no matter how correct yourInfo.plistis:
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
target.build_configurations.each do |config|
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)']
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] << 'PERMISSION_CAMERA=1'
end
end
end
Enable the camera only. The flow never needs anything else, and shipping unused permission code invites App Store review questions.
The microphone is never requested: the portal captures with
getUserMedia({ video: ..., audio: false }).
At runtime the SDK requests the OS camera permission and then grants it to the WebView. If the user denies it, the portal shows its own "camera unavailable" message.
How it talks to the portal #
The web SDK is a parent page holding an iframe, so the portal's
window.parent.postMessage(...) crosses a frame boundary. A Flutter WebView
has no parent frame — window.parent === window — so this SDK reports the
portal's own origin as hostOrigin. The portal's messages are then
delivered back to its own window, where a script injected at document start
forwards them to Dart.
That matters because the portal treats a non-empty hostOrigin as "SDK mode"
(isSdkEnabled = !!thirdPartyOrigin); without it, it never emits
OnboardingCompleted at all.
The script intercepts window.postMessage rather than merely listening.
The portal's handshake stream completes on the first message whose origin
matches hostOrigin, so its own outbound request — echoed back because
window.parent is the page itself — would end that stream before any reply
arrived, and the portal would abandon the session-token exchange and show its
login screen. Portal → host events therefore go to Dart instead of being
re-delivered into the page; only the reply is delivered.
Supported events: OnboardingCompleted, IframeParametersRequest /
IframeParametersResponse, and TenantBaseUrl (vanity-domain redirect).
ResizeIframe is received and ignored — a WebView fills its own box and the
portal scrolls inside it, so there is no outer frame to resize.
Example #
example/ is a harness with a session-id field, region and environment
pickers, the layout and language toggles, and a live view of the resolved
hosts. Run it on a physical device — camera capture does not work in a
simulator.
cd example
flutter pub get
flutter run
Integration tests #
example/integration_test/ drives the real app against the live portal — the
only way to cover what unit tests cannot, namely that the portal's
postMessage actually reaches Dart inside a WebView.
cd example
flutter test integration_test/bridge_test.dart -d <device> # handshake round-trip
flutter test integration_test/tls_test.dart -d <device> # bad cert is rejected
Both pass on an Android emulator and an iOS simulator. bridge_test needs
network access and hits app.azakaw.com.
Upgrading from 0.0.x #
See CHANGELOG.md. 1.0.0 is a breaking release: the Environment enum is
replaced by AzakawRegion + AzakawEnvironment, and the sandbox host has
changed.
Support #
software@azakaw.com · docs.azakaw.com
Licensed under a commercial agreement — see LICENSE.