okyc_flutter 0.1.0
okyc_flutter: ^0.1.0 copied to clipboard
OKYC identity verification for Flutter: the hosted KYC/KYB flow in a secure, full-screen WebView with native permissions, file picking and results.
okyc_flutter #
Flutter SDK for OKYC. It opens a verification session's hosted flow in a hardened WebView and returns a typed result. It follows the shared mobile SDK contract: protocol v1 bridge, result mapping and WebView rules. Full guide: docs.okyc.stream/mobile/flutter.
- Android and iOS (Flutter ≥ 3.24, Dart ≥ 3.5, Android minSdk 24, iOS 15).
- Built on
webview_flutter4.x plus a small native plugin that handles screenshot blocking, Android camera permission and the Android file chooser.
Install #
flutter pub add okyc_flutter
iOS #
Set the minimum iOS version to 15.0 (Podfile platform :ios, '15.0', or the Runner target's deployment target when using Swift Package Manager). Then add the camera strings to Info.plist:
<key>NSCameraUsageDescription</key>
<string>Take a selfie and photograph your documents to verify your identity.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Choose a photo of your document to verify your identity.</string>
Android #
The plugin's manifest already declares INTERNET and CAMERA, the <queries> for camera and document pickers, and a FileProvider scoped to cache/okyc/. Nothing else is needed.
Usage #
Your server creates the session with its secret key (POST /v1/sessions) and sends the app only the url. Never ship a secret key in the app.
import 'package:okyc_flutter/okyc_flutter.dart';
final result = await Okyc.start(
context,
OkycConfig(sessionUrl: url /* https://verify.okyc.stream/s/ses_…#t=st_… */, locale: 'en'),
onEvent: (e) => debugPrint('${e.type} ${e.payload}'), // analytics only
);
switch (result) {
case OkycCompleted(:final sessionId, :final status):
// status is a hint (`approved`, `rejected`, `in_review`…). Trust only your server:
// the session.completed webhook, or GET /v1/sessions/{id}.
await myBackend.refreshKyc(sessionId);
case OkycCancelled():
// The user closed the flow. The same link can be reopened while it is valid.
break;
case OkycFailed(:final code, :final message):
// OkycFailureCode.invalidConfig | loadFailed | networkError | navigationBlocked | permissionDenied | flowError
showError(message);
}
Okyc.start pushes a full-screen route with an ✕ close button. Both ✕ and the Android back gesture ask "Exit verification?" before returning OkycCancelled. The route closes itself about 2 s after the flow reports complete, so the user sees the result screen.
If you want to place the flow in your own layout, embed OkycVerification(config:, onResult:) directly. Your app then owns the navigation and the exit confirmation.
OkycConfig #
| Field | Default | |
|---|---|---|
sessionUrl |
required | https://…/s/ses_…#t=st_…. Plain http is accepted only for localhost, 127.0.0.1, 10.0.2.2 and ::1. |
locale |
null |
Sent as ?locale=. The token stays in the URL fragment. |
theme |
system |
Sent as ?theme=; light or dark forces the flow's theme. |
allowedHosts |
[] |
Extra hosts (or https://host:port origins) allowed inside the WebView. |
blockScreenshots |
true |
Android FLAG_SECURE. On iOS, a privacy cover in the app switcher and while the screen is recorded. |
autoCloseDelay |
2s |
Time between complete and the result. |
confirmExit |
true |
Ask before closing. |
title |
Verify your identity |
App bar title. |
Security behaviour #
-
Bridge. Messages arrive on the
OkycFlutterJavaScript channel. They are dropped if they:- are larger than 8 KB,
- don't match
{source:'okyc', v:1}, - have an unknown
type, - carry a different session id, or
- have a nested payload.
Callbacks never receive PII, only step names and statuses.
-
Navigation. The WebView may only visit:
- the session's own origin,
- DigiLocker (
digilocker.meripehchaan.gov.in,*.digilocker.gov.in,api.digitallocker.gov.in), and - your
allowedHosts.
Other
http(s)links open in the system browser. Every other scheme is refused, includingfile:,javascript:,data:,intent:andtel:. A run of refused navigations (more than 5 in 3 s) ends the flow withnavigation_blocked. -
Camera. Access is granted only while the WebView shows the session origin. On Android it is also granted only after the OS permission. iOS asks through WebKit (
allowsInlineMediaPlayback, and no user gesture is required for media). -
Files. On Android,
<input type=file>opens the system picker, with a camera option. Withcapture, it opens the camera directly. Captures stay incache/okyc/. -
Cleanup. When the flow finishes, the SDK clears
sessionStorage,localStorageand the WebView cache, and turns screenshot blocking off. -
Debugging. WebView debugging (Chrome and Safari inspectors) is on only in debug builds.
Local development #
With the API on :4000 and the verify app on :5173, run:
cd sdks/flutter/example
flutter run # "Create sandbox session" signs up a throwaway local account and creates a test session
The Android emulator reaches the host at 10.0.2.2. The example rewrites localhost for you, and its network security config allows cleartext only to 10.0.2.2 and localhost.
Tests #
cd sdks/flutter
flutter analyze
flutter test # unit + widget tests (fake WebView)
cd example && flutter build apk --debug && flutter build ios --simulator --debug
flutter test integration_test/walkthrough_test.dart -d <simulator> # drives a sandbox session in the real WebView
walkthrough_test.dart creates a sandbox session against the local API (or uses --dart-define=OKYC_SESSION_URL=…). It fills and taps the hosted pages over JavaScript through the real SDK host, and prints SNAP:<name> after each step so an outside script can take screenshots.
Limitations #
- The minimum is Flutter 3.24, not the 3.22 in the contract table, because the current
webview_flutterplatform packages need Dart 3.5. - iOS can't block user screenshots. The SDK hides content in the app switcher and during screen recording or mirroring.
- On Android,
clearCacheclears the whole app's WebView cache, not just the flow's. Cookies are left alone: the flow uses a bearer token, and clearing cookies would affect the whole app. - The verify app doesn't read
?locale=yet, so it follows the device language. - This package's directory is named
flutter. With Swift Package Manager, the Flutter tool links it as.packages/flutter(the package identity comes from the folder name). That works as long as the app has no other local package reference toios/okyc_flutter.