Skip to content

Adding identity verification to a React Native or Flutter app

The component call is the easy part. A mobile integration is mostly native build setup, camera permission, mounting a dashboard workflow so changes skip the app store, and keeping results on your server rather than on the phone.

Charles Archibong

, Co-founder

· 7 min read

Headline "Verification in a mobile app" beside an illustration of a phone showing a face capture guide, on a deep indigo gradient.

Key takeaways

  • The React Native SDK ships native code, so it needs a dev client or bare build and does not run in Expo Go.
  • Mount a published workflow by ID so flow changes are a re-publish rather than an app-store release.
  • onSubmit means the verification was accepted; read the result on your backend with a secret key.
  • NFC chip reading works only in the mobile SDKs, and a phone without NFC skips the step without failing.

Integrating identity verification into a React Native or Flutter app is a few lines of code and a fair amount of native setup. The Myaza Trust SDKs render the whole flow (consent, ID selection, document capture, selfie and active liveness) and submit it for you. What you own is the build configuration, the camera permission, the key you ship, and what your app does after the applicant presses submit.

The approach that ages best is to mount a workflow built in the dashboard, pass only per-user data from code, and read results on your server. That way a change to the flow is a re-publish rather than an app-store release, and no identity data needs to come back to the phone. The rest of this article covers each step and the mobile-specific decisions along the way.

What are you actually adding to the app?

Both SDKs run on-device native liveness: Apple Vision on iOS and Google ML Kit on Android. That is the reason for most of the setup. Liveness here is Presence Intelligence (a live selfie plus randomised gesture challenges, a screen-flash colour sequence, or both), and it runs on camera frames in native code rather than in a web view.

The two SDKs are at feature parity with the web SDK for the core flow, workflow embedding, the capture add-ons (contact OTP, proof of address, questionnaire), business verification and any global documents country. They add one thing the web cannot do: reading the chip in e-passports and chip ID cards over NFC.

What do the builds require?

The minimums, from the React Native and Flutter SDK pages:

Requirement

React Native

Flutter

Package

@myazahq/kyc-sdk-react-native

myaza_kyc_sdk_flutter (^2.2.0)

Framework

React Native 0.83+ with the New Architecture; Expo SDK 56

Flutter 3.27 (Dart 3.6)

iOS deployment target

15.1

13.0

Android minSdkVersion

24

21

Runtime

A dev client or bare build, not Expo Go

Any Flutter build

Workflow mounting from

SDK 2.1.0

SDK 2.2.0

The React Native line is the one that catches teams out. The SDK uses a VisionCamera v5 (Nitro) frame processor, which is native code, so it cannot run inside Expo Go. Plan for a custom dev client from the first day of integration, not the day before release.

How do you install it?

For an Expo app on React Native, install the SDK and its peers, add the config plugin, and build a dev client:

npx expo install @myazahq/kyc-sdk-react-native \
  react-native-vision-camera react-native-vision-camera-worklets \
  react-native-worklets react-native-nitro-modules react-native-nitro-image \
  react-native-safe-area-context react-native-svg
// app.json
{
  "expo": {
    "plugins": ["@myazahq/kyc-sdk-react-native"]
  }
}
npx expo prebuild
npx expo run:ios       # or: npx expo run:android

Do not add VisionCamera itself to plugins. Version 5 ships no config plugin, and listing it makes expo prebuild fail. The Myaza plugin adds the iOS camera usage string and the Android CAMERA and INTERNET permissions for you. Bare React Native apps add the Expo module runtime with npx install-expo-modules@latest and declare the permissions by hand; the SDK page has the exact commands.

For Flutter, add the dependency and run flutter pub get:

dependencies:
  myaza_kyc_sdk_flutter: ^2.2.0

Then declare the camera permission yourself: NSCameraUsageDescription in ios/Runner/Info.plist, and CAMERA plus INTERNET in AndroidManifest.xml.

Which permissions will your users see?

Camera, always. Location, only if a workflow uses the Address Intelligence step. The React Native config plugin adds the location strings by default (pass { "location": false } to opt out); in Flutter and bare React Native you add them yourself. On iOS the app crashes on the location request if NSLocationWhenInUseUsageDescription is missing, so add it whenever an address step is possible. Microphone, never: voice guidance is text-to-speech output and the SDK does not record audio. That last point is worth putting in your app-store privacy answers.

Which optional React Native modules matter?

Four expo-* modules are optional peers, and each buys a capability. The one most teams miss is expo-document-picker: without it, the proof-of-address and business document steps accept a camera capture only, so a PDF bank statement cannot be submitted at all. expo-device improves the device fingerprint used by device intelligence, expo-application records your app's version on the submission, and expo-localization gives the SDK the device's region.

How should you mount the flow?

Mount a published workflow by its ID. The workflow carries the country, ID types, capture steps, branding and copy, and it can carry server-side decision rules too. React Native:

import { MyazaKYC } from "@myazahq/kyc-sdk-react-native";

export default function VerifyScreen() {
  return (
    <MyazaKYC
      apiKey="pk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
      workflowId="wf_AbC123dEf456"
      userId="user_42"
      userData={{ firstName: "Jane", lastName: "Doe" }}
      metadata={{ requestId: "order_1001" }}
      onSubmit={(submission) => console.log("submitted", submission.verificationId)}
      onError={(err) => console.error(err.code, err.message)}
      onClose={() => console.log("closed")}
    >
      Verify Identity
    </MyazaKYC>
  );
}

Flutter opens the same flow as a modal sheet. Note that context is a named parameter and the callbacks sit beside config, not inside it:

MyazaKYC.show(
  context: context,
  config: const MyazaKYCConfig(
    apiKey: 'pk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
    workflowId: 'wf_AbC123dEf456',
    userId: 'user_42',
    userData: UserData(firstName: 'Jane', lastName: 'Doe'),
    metadata: {'requestId': 'order_1001'},
  ),
  onSubmit: (submission) => debugPrint('Submitted: ${submission.verificationId}'),
  onError: (error) => debugPrint('Error: ${error.code}: ${error.message}'),
  onClose: () => debugPrint('KYC closed'),
);

Three details in those snippets are deliberate.

The key is publishable. Only a pk_ key belongs in an app. Flutter derives the environment from its prefix (pk_test_ is Sandbox, pk_live_ is Production), so switching to production is a key change. A publishable key can start a verification but cannot read its result.

userId, userData and metadata stay in code. A workflow is a template shared by every visitor, so it cannot carry per-user values. userData is worth passing: it is the name you believe the user has, compared against the name read off their document to produce dataMatch. Leave it out and dataMatch comes back null.

metadata.requestId should come from your own record. It is the idempotency key for the submission, so derive it from the onboarding record it belongs to rather than generating a new one each time the screen mounts.

Why a workflow rather than props? Both SDKs accept the full configuration as props, and that works for a fixed flow. But on mobile every change to props is an app release and a review cycle, and old app versions stay installed for months. With a workflow, adding an ID type or tightening a face-match threshold reaches every installed version the next time the flow opens. The Workflows product page describes the builder.

What happens after the applicant submits?

onSubmit fires when the server accepts the verification. It is not the result. The checks run asynchronously, and the outcome reaches your backend by webhook, or by a secret-key call to GET /api/kyc/verifications/:id. onError fires for technical errors only (network, authentication, credit, upload), never for a declined verification.

So the pattern on mobile is: on onSubmit, send the verificationId to your backend, show a "we are checking your details" screen, and let your backend tell the app what to show next when the webhook arrives. Do not fetch the full result from the app. It requires a secret key, and a secret key in an app binary is a secret key someone else has.

When does NFC chip reading make sense?

When your applicants hold e-passports or chip ID cards and you want the strongest assurance level. The mobile SDKs read the chip over its secure channel, and the server runs passive authentication: it rechecks each data group's hash against the chip's signed security object and verifies that the signer chains to a trusted issuing-state certificate (CSCA). A chip that proves to be tampered fails the verification with chip_not_authentic.

Four limits to design around, from the NFC chip documentation:

  • Coverage follows certificates. The chip step is offered only for countries on Myaza's certificate list, which covers 138 countries including Nigeria, Ghana, Kenya, Egypt, Ethiopia and Senegal. GET /config reports supportsNfc per ID type for your account.

  • Some chips cannot be read by anyone. The South African smart ID card, the Emirates ID and Malaysia's MyKad use proprietary chip applications. Those documents still verify through document checks.

  • Phones without NFC skip the step, and skipping never fails a verification. A missing or unreadable chip falls back to the document and government-record checks.

  • The chip step runs after the document scan, because the key that opens the chip's secure channel is derived from the document's machine-readable zone.

Which chip-capable IDs offer the step, and whether users may skip it, are workflow settings in the builder's NFC panel.

How do you keep the app size reasonable?

On Android, face detection and text recognition use Google ML Kit, and the SDK depends on the Play Services variants that download their models on first use rather than bundling them. Bundling them adds a sizeable download to every device, which is why the fetched variants are the default. The trade-off is that Play Services variants do not work on devices without Google Play Services, such as Huawei. If you ship to those, set ext { myazaKycBundledMlKit = true } in your root build.gradle to bundle the models instead.

Shipping an Android App Bundle rather than a universal APK, and enabling R8 with resource shrinking, reduce the download further. The SDK ships its own consumer rules for R8.

Checklist before release

  • React Native: a dev client or bare build in CI, never Expo Go; New Architecture enabled.

  • Camera permission declared; location string declared if any workflow collects an address; no microphone permission.

  • expo-document-picker installed if any workflow collects documents as files.

  • A published workflow mounted by ID; userId, userData and a stable metadata.requestId passed from code.

  • A pk_test_ key in development builds and a pk_live_ key in release builds, never a secret key.

  • onSubmit hands the verificationId to your backend; results come from webhooks.

  • NFC coverage checked against supportsNfc for the countries you serve.

Test the whole path in Sandbox first with published test IDs, then swap the key. The developer hub links both SDK guides.

Sources

Charles Archibong

About the author

Charles Archibong

Co-founder

Charles Archibong co-founded Myaza Trust. He writes about identity verification, financial technology, and the practical work of building trusted digital services.

  • Headline "What a passport chip proves" beside an illustration of a passport booklet with a chip symbol and contactless waves, on a soft lavender gradient.

    Identity Verification

    What reading an ePassport chip proves, and what it can't

    Reading an ePassport chip can prove the data was signed by the issuing state and has not been altered, and with Active Authentication that the chip is not a copy. It cannot prove the person holding the passport is its owner; only a face match against the chip photo does that.

  • Headline "Testing verification in sandbox" beside an illustration of a laboratory flask with bubbles, on a deep indigo gradient.

    Developers

    Testing a verification integration end to end in sandbox

    Use the published sandbox test IDs, whose last digits pick the outcome, to drive every result your code must handle, then test your webhook receiver with signed simulated events before switching to live keys.

Build your product.We'll handle the rest.

Identity and compliance, end to end, built to global standards, priced for founders.

Identity verification in React Native and Flutter apps · Myaza Trust