Keyri React Native SDK
System Requirements
-
Android API level 23 or higher
-
AndroidX compatibility
-
iOS 14+
-
Swift 5+
-
Apple A7 chip or newer (The A7 shipped with the iPhone 5s)
Integration
npm (opens in a new tab) and yarn (opens in a new tab) can be used to incorporate the Keyri SDK into your React Native project. To install the Keyri library, run this command in terminal in root of the project directory:
yarn add react-native-keyri
OR
npm i react-native-keyri
The SDK can then be imported into any js or ts file as follows:
import Keyri from 'react-native-keyri';
Option 1 - Universal Links (iOS)
To handle Universal Links (e.g., for QR login straight from the user's built-in camera app), you need to add the Associated Domains Entitlement to your App.entitlements file. To set up the entitlement in your app, open the target’s Signing & Capabilities tab in Xcode and add the Associated Domains capability, or if you already have entitlements you can modify your App.entitlements file to match this example:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:{domainName}</string>
</array>
</dict>
</plist>
This will handle all links with the following scheme: https://{yourCompany}.onekey.to?sessionId={sessionId}
Note: Keyri will set up the required /.well-known/apple-app-site-association
JSON at your https://{yourSubdomain}.onekey.to
page as required by Apple to handle Universal Link handling. Details on this mechanism are described here: https://developer.apple.com/documentation/Xcode/supporting-associated-domains (opens in a new tab)
Option 1 - App Links (Android)
To handle Android App Links (e.g., for QR login straight from the user's built-in camera app) you need to define the following intent-filter block in your AndroidManifest.xml
:
<application...>
<!-- ... -->
<activity...>
<!-- ... -->
<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="${domainName}" android:scheme="https" />
</intent-filter>
</activity>
</application>
This will handle all links with the following scheme: https://{yourCompany}.onekey.to?sessionId={sessionId}
Note: Keyri will set up the required /.well-known/assetlinks.json
JSON at your https://{yourSubdomain}.onekey.to
page as required by Android App Links handling. Details on this mechanism are described here: https://developer.android.com/training/app-links/verify-site-associations (opens in a new tab)
JS implementation of universal links handling
Follow this guide (opens in a new tab) for Universal Links handling in react-native. And add this code in the processing step of your handleDeepLink
function.
useEffect(() => {
const handleUrl = ({ url }: { url: string }) => {
setDeepLink(url);
};
Linking.addEventListener('url', handleUrl);
return () => Linking.removeAllListeners('url');
}, []);
const getInitialUrl = async () => {
const initialUrl = await Linking.getInitialURL();
if (initialUrl) {
setDeepLink(initialUrl);
}
};
getInitialUrl();
Note: Keyri will create your https://{yourCompany}.onekey.to
page automatically once you configure it in the dashboard (opens in a new tab)
Option 2 - In-App Scanner
Use authWithScanner
built-in functionality to delegate authentication to SDK. Call the method for authWithScanner
and pass optional public user ID, and payload:
import Keyri from 'react-native-keyri';
const InitialScreen: React.FC<InitialScreenProps> = ({ route }) => {
const onReadSuccess = async (scan: BarCodeReadEvent) => {
const params: ISearchParam = parseUrlParams(scan.data);
const sessionId: string = params?.sessionId ?? '';
const options = {
sessionId: sessionId,
publicUserId: ''
};
await Keyri.initiateQrSession(options);
await Keyri.initializeDefaultConfirmationScreen('payload');
};
//add your code for scanning QR code and on success scan call onReadSuccess method
};
Or define your own custom scanner UI/UX. You can use Firebase ML Kit, ZXing, your own scanner, or any other equivalent. All you need to do is convert the code you just scanned to a URI, and then you're free to process the response the same way we did above (notice the process(uri)
function is exactly the same in both cases)
import Keyri from 'react-native-keyri';
const InitialScreen: React.FC<InitialScreenProps> = ({ route }) => {
const [session, setSession] = React.useState<KeyriSession | null>(null);
const [sessionId, setSessionId] = React.useState<string>('');
const [customLoginVisible, setCustomLoginVisible] = React.useState<boolean>(false);
const onReadSuccess = async (scan: BarCodeReadEvent) => {
const params: ISearchParam = parseUrlParams(scan.data);
const options = {
sessionId: params?.sessionId,
publicUserId: ''
};
const activeSession = await Keyri.initiateQrSession(options);
setSession(activeSession);
setCustomLoginVisible(true);
setSessionId(params?.sessionId);
}
return (
<View style={styles.root}>
<PopupModal
session={session}
sessionId={sessionId}
isVisible={customLoginVisible}
/>
</View>
);
}
const PopupModal: React.FC<IPopupModalProps> = ({
session,
sessionId,
isVisible
}) => {
// get any data you need from session object (detailed information is below)
// in the Session Object section.
// render your custom content modal and custom confirm / deny buttons
return (
<Modal visible={isVisible}>
//render content based on session object, e.g. location, country code
<View style={styles.customPopupButtonView}>
<TouchableOpacity
style={[styles.noBtn, styles.btn]}
onPress={() => { Keyri.denySession('custom_payload'); }>
<Text style={[styles.touchableText]}>Deny</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.yesBtn, styles.btn]}
onPress={() => { Keyri.confirmSession('custom_payload', true); }>
<Text style={[styles.touchableText, styles.white]}>Confirm</Text>
</TouchableOpacity>
</View>
</Modal>
);
}
Interacting with the API
The following methods are available to interact with the Keyri SDK API, which can be used to craft your own custom flows and leverage the SDK in different ways:
-
initialize: (options: InitializeKeyriOptions) => Promise<boolean>
- call it before all Keyri operations -
easyKeyriAuth: (publicUserId: string, payload: string) => Promise<boolean>
- handle process flow with passed scanned url and showing default confirmation screen. Easiest way to process session from deeplink -
login: (publicUserId?: string) => Promise<LoginObject>
- call this method to generate object for login which includes timestampNonce, signature, publicKey and userId -
register: (publicUserId?: string) => Promise<RegisterObject>
- call this method to generate object for register which includes publicKey and userId -
processLink: (options: ProcessLinkOptions) => Promise<boolean>
- process flow with passed uri with showing default confirmation screen. Easiest way to process session from deeplink. Returns result of authentication or error -
initiateQrSession: (sessionId: string, publicUserId?: string) => Promise<KeyriSession>
- call it after obtaining the sessionId from QR code or deep link. Returns Session object with Risk attributes (needed to show confirmation screen) or Exception -
initializeDefaultConfirmationScreen: (payload: string) => Promise<boolean>
- to show Confirmation with default UI. Returns Boolean result. Also, you can implement your custom Confirmation Screen, just inherit from BaseConfirmationDialog.kt -
confirmSession: (payload: string, trustNewBrowser?: boolean) => Promise<boolean>
- call this function if user confirmed the dialog, trustNewBrowser is false by default. Returns Boolean authentication result -
denySession: (payload: string) => Promise<boolean>
- call if the user denied the dialog. Returns Boolean authentication result -
generateAssociationKey: (publicUserId?: string) => Promise<string>
- creates a persistent ECDSA keypair for the given public user ID (example: email address) and returns public key -
generateUserSignature: (data: string, publicUserId?: string) => Promise<string>
- returns an ECDSA signature of the timestamp and optional data with the publicUserId's privateKey (or, if not provided, anonymous privateKey), data can be anything -
listAssociationKeys: () => Promise<string[]>
- returns a list of names (publicUserIds) of "association keys" (public keys) -
listUniqueAccounts: () => Promise<string[]>
- returns a list of association keys except key for Anon user -
getAssociationKey: (publicUserId?: string) => Promise<string>
- returns Base64 public key for the specified publicUserId -
removeAssociationKey: (publicUserId: string) => Promise<boolean>
- removes public key for the specified publicUserId -
sendEvent: (data: SendEventOptions) => Promise<KeyriFingerprintEventResponse>
- sends fingerprint event with specified event type and result. Returns the event risk response object from the Keyri API -
createFingerprint: () => Promise<FingerprintEventRequest>
- creates and returns fingerprint event object
Session Object
The session object is returned on successful initiateQrSession
calls, and is used to handle presenting the situation to the end user and getting their confirmation to complete authentication. Below are some of the key properties and methods that can be triggered. If you are utilizing the built-in views, you are only responsible for calling the confirm/deny methods above
-
iPAddressMobile/Widget - The IP Address of both mobile device and web browser
-
riskAnalytics - if applicable
-
riskStatus - clear, warn or deny
-
riskFlagString - if RiskStatus is warn or deny, this string alerts the user to what is triggering the risk situation
-
geoData - Location data for both mobile and widget
-
mobile
-
city
-
country_code
-
browser
-
city
-
country_code
-
-
mobileTemplateResponse - Use this object to define confirmation screen UI
-
confirmSession()
anddenySession()
- see descriptions in Interacting with the API.
Disclaimer
We care deeply about the quality of our product and rigorously test every piece of functionality we offer. That said, every integration is different. Every app on the App Store has a different permutation of build settings, compiler flags, processor requirements, compatibility issues etc and it's impossible for us to cover all of those bases, so we strongly recommend thorough testing of your integration before shipping to production. Please feel free to email us at support@keyri.com to submit a bug report or feature request