fia-react
v1.0.0
Published
FIA React Native by Fazpass.
Readme
fia-react
FIA React Native by Fazpass. Visit the official website for more information about the product, and see the github documentation for more technical details.
Wraps the native FIA SDKs: Android com.fazpass:fia:1.3.1 and iOS FiaIOS ~> 1.3.2.
Installation
npm install fia-reactiOS setup
The iOS SDK stores state in an App Group, so this is required — initialize() needs the container id.
In Xcode, under Signing & Capabilities, add:
- App Groups — add a container named
group.YOUR_INVERTED_DOMAIN - iCloud — enable the Key-value storage service
For the MagicOtp and MagicLink auth types, add to Info.plist:
<key>LSApplicationQueriesSchemes</key>
<array>
<string>whatsapp</string>
<string>whatsappbusiness</string>
</array>For MagicLink you must also add an Associated Domains capability (applinks:YOUR_DOMAIN), serve an
apple-app-site-association file from that domain, and forward the incoming link to the SDK from your
AppDelegate — without this the link never reaches FIA and validation never completes:
import FiaReact
override func application(
_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
) -> Bool {
FiaReactLinking.onMagicLink(userActivity: userActivity)
return super.application(application, continue: userActivity, restorationHandler: restorationHandler)
}Android setup
For the Miscall auth type, add to your AndroidManifest.xml and request at runtime:
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_CALL_LOG" />For the HE auth type, add to your <application> tag:
android:networkSecurityConfig="@xml/fia_network_security_rules"This will break Metro in debug builds unless you also do the following. Android ignores
android:usesCleartextTrafficwhenever a network security config is present, and FIA's config only permits cleartext for the Fazpass domains — notlocalhost. Your debug app then cannot reach the Metro dev server, and fails with a misleadingUnable to load scripterror.Fix it by giving debug builds their own config that keeps FIA's domains and adds Metro's. Create
android/app/src/debug/res/xml/debug_network_security_config.xml:<?xml version="1.0" encoding="utf-8"?> <network-security-config> <!-- Metro: localhost via `adb reverse`, 10.0.2.2 on the emulator. --> <domain-config cleartextTrafficPermitted="true"> <domain includeSubdomains="false">localhost</domain> <domain includeSubdomains="false">10.0.2.2</domain> </domain-config> <!-- Copied from the FIA SDK's rules, needed for Header Enrichment. --> <domain-config cleartextTrafficPermitted="true"> <domain includeSubdomains="true">verify.klikaman.online</domain> <domain includeSubdomains="true">api.fazpass.com</domain> <domain includeSubdomains="true">openapi.smartfren.com</domain> <trust-anchors> <certificates src="system" /> <certificates src="user" /> </trust-anchors> </domain-config> </network-security-config>and point debug builds at it in
android/app/src/debug/AndroidManifest.xml:<application android:networkSecurityConfig="@xml/debug_network_security_config" tools:replace="android:networkSecurityConfig" />See
example/android/app/src/debug/for a working copy.
For the MagicLink auth type, declare the SDK's activity inside <application> (and serve
assetlinks.json from your domain):
<activity
android:name="com.fazpass.fia.activities.magiclink.MagicLinkActivity"
android:exported="true">
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:host="YOUR_DOMAIN" android:scheme="https" />
</intent-filter>
</activity>Usage
import FIA, { OtpPromise, OtpAuthType, OtpMagicRedirect, FiaErrorType } from 'fia-react';
// Initialize once, before anything else.
// appGroupId is required on iOS and ignored on Android.
FIA.initialize('MERCHANT_KEY', 'MERCHANT_APP_ID', 'group.com.example.app');
// Request an OTP. Use the method matching your use case:
// login(), register(), transaction() or forgetPassword().
let otpPromise: OtpPromise;
FIA.otp().login('PHONE', (promise) => {
if (promise.hasException) {
console.error(promise.error?.type, promise.error?.message);
return;
}
if (promise.isBlocked) {
// The user failed verification too many times and is temporarily blocked.
return;
}
otpPromise = promise;
});otpPromise.authType tells you how the user is being verified. Handle all of them:
switch (otpPromise.authType) {
case OtpAuthType.He:
// Nothing to input — just validate.
otpPromise.validateHE(onError, onValidated);
break;
case OtpAuthType.SMS:
case OtpAuthType.Whatsapp:
case OtpAuthType.Voice:
// Ask the user for the code (otpPromise.digitCount digits), then:
otpPromise.validate('USER_INPUT', onError, onValidated);
break;
case OtpAuthType.Miscall:
// The user types the last `digitCount` digits of the caller's number.
// On Android you can read them automatically instead:
otpPromise.listenToMiscall(onError, (otp) => {
otpPromise.validate(otp, onError, onValidated);
});
break;
case OtpAuthType.MagicOtp:
// Sends the user to WhatsApp; they come back with a code you then validate().
otpPromise.launchWhatsappForMagicOtp(onError, () => {
// show an input, then otpPromise.validate(...)
});
break;
case OtpAuthType.MagicLink:
// Sends the user to WhatsApp; they tap a link and validation completes on its own.
otpPromise.launchWhatsappForMagicLink(onError, onValidated);
break;
}When you're done with a promise, free it:
otpPromise.clean();A validated OTP does not by itself mean the user is verified. Check the verified status server-side using
otpPromise.transactionId— see the server documentation.
Letting the user choose the auth type
FIA.otp() lets the server decide which auth type to use. FIA.otpManual() instead returns every
auth type available for the phone number, so the user can pick the one they prefer. It takes the
same four methods — login, register, transaction, forgetPassword.
import FIA, { OtpGatewayPromise } from 'fia-react';
let gatewayPromise: OtpGatewayPromise;
FIA.otpManual().login('PHONE', (promise) => {
if (promise.hasException) {
console.error(promise.error?.type, promise.error?.message);
return;
}
if (promise.isAuthenticated) {
// The user needs no OTP at all. The flow ends here: take promise.transactionId and check
// the verified status. promise.gateways is empty.
promise.clean();
return;
}
gatewayPromise = promise;
});Show gatewayPromise.gateways to the user. Each OtpGateway has a name to display and a
number to pass back. Picking one hands you the same OtpPromise the automatic flow produces, so
it feeds straight into the authType switch above:
gatewayPromise.pick(gateway.number, (otpPromise) => {
if (otpPromise.hasException) {
console.error(otpPromise.error?.type, otpPromise.error?.message);
return;
}
// switch (otpPromise.authType) { ... } exactly as above
gatewayPromise.clean();
});Free the gateway promise with gatewayPromise.clean() once you are done with it, the same way you
free an OtpPromise.
Options
login, register, transaction and forgetPassword all take an optional third argument, on both
FIA.otp() and FIA.otpManual():
FIA.otp().login('PHONE', callback, {
// Forwarded to the FIA backend with the request.
additionalInfo: { 'url': 'https://example.com' },
});Magic redirect
Which WhatsApp app to open is set on the launch call itself:
otpPromise.launchWhatsappForMagicOtp(onError, onSuccess, {
magicRedirect: OtpMagicRedirect.WhatsappBusiness,
});The same option applies to launchWhatsappForMagicLink. It defaults to OtpMagicRedirect.Auto,
which picks WhatsApp or WhatsApp Business automatically; use OtpMagicRedirect.Manual to let the
user choose when both are installed.
Errors
Every error callback receives a FiaError with a type (FiaErrorType) and a message:
otpPromise.validate('123456', (error) => {
if (error.type === FiaErrorType.VerificationFailed) {
// wrong or expired code
}
}, onValidated);OtpPromise and OtpGatewayPromise both carry the same hasException / error: FiaError pair, so
a failed otp request and a failed gateway request are read the same way.
Pro-features
FIA.setFeatures({
withLocation: true,
withOtpSpammingFunction: true,
withBiometricPopup: true,
biometricMessage: 'Confirm it is you',
withAccountTakeoverFunction: true,
userIdentifier: 'user-123',
withPromoAbuseFunction: true,
promoIds: ['PROMO_1'],
// Android only — the iOS SDK does not implement these:
withVpn: true,
withSimNumbersAndOperators: true,
withAppTamperingFunction: true,
withSuspiciousAppFunction: true,
});Platform differences
| | Android | iOS |
|---|---|---|
| initialize() appGroupId | ignored | required |
| listenToMiscall() | supported | calls onError — iOS cannot read the caller's number |
| otpManual(), pick() | supported | supported |
| withVpn, withSimNumbersAndOperators, withAppTamperingFunction, withSuspiciousAppFunction | supported | ignored |
| MagicLink | manifest entry | FiaReactLinking.onMagicLink in AppDelegate |
