Skip to content

Commit f008c98

Browse files
Merge pull request #3 from TransmitSecurity/feature/MOB-1140
MOB-1140 - Update native SDK versions, implement biometric authentication
2 parents d11f1fc + 7a658e0 commit f008c98

File tree

16 files changed

+520
-66
lines changed

16 files changed

+520
-66
lines changed

README.md

Lines changed: 95 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,13 @@ To integrate this module, you'll need to configure an application.
4040
npm install react-native-ts-authentication
4141
```
4242

43-
#### iOS Setup
43+
### iOS Setup
4444
You might need to execute `pod install` in your project's `/ios` folder and set your minimum iOS target to 15.0 in your Podfile (e.g `platform :ios, 15.0`).
4545

4646
* Add project Capabilities as described [iOS quick start](https://developer.transmitsecurity.com/guides/webauthn/quick_start_sdk_ios/)
4747
* Update YOUR Bundle ID and setup associated domains as described in the [iOS quick start](https://developer.transmitsecurity.com/guides/webauthn/quick_start_sdk_ios/)
4848

49-
#### Android Setup
49+
### Android Setup
5050

5151
Add to `app/build.gradle` under repositories
5252

@@ -58,16 +58,48 @@ repositories {
5858
}
5959
}
6060
```
61-
Note:
62-
As for projects on Gradle 8+ and Kotlin 1.8+ build will fail if the JDK version between
63-
compileKotlin and compileJava and jvmTarget are not aligned.
64-
65-
This won't be necessary anymore from React Native 0.73. More on this:
66-
https://kotlinlang.org/docs/whatsnew18.html#obligatory-check-for-jvm-targets-of-related-kotlin-and-java-compile-tasks
61+
#### Note:
62+
As for projects on Gradle 8+ and Kotlin 1.8+ build will fail if the JDK version between compileKotlin and compileJava and jvmTarget are not aligned.
63+
<br>
64+
This won't be necessary anymore from React Native 0.73. More on this: https://kotlinlang.org/docs/whatsnew18.html#obligatory-check-for-jvm-targets-of-related-kotlin-and-java-compile-tasks
6765

6866
## Usage
6967

70-
#### Module Setup
68+
### Module Setup
69+
70+
#### iOS
71+
1. Open your project's `.xcworkspace` found under `YOUR_PROJECT_PATH/iOS` in Xcode.
72+
2. Create a plist file named TransmitSecurity.plist in your Application with the following content. CLIENT_ID is configured in your Transmit server. Make sure the file is linked to your target.
73+
74+
```xml
75+
<?xml version="1.0" encoding="UTF-8"?>
76+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
77+
<plist version="1.0">
78+
<dict>
79+
<key>credentials</key>
80+
<dict>
81+
<!-- Use api.eu.transmitsecurity.io for EU, api.ca.transmitsecurity.io for CA -->
82+
<key>baseUrl</key>
83+
<string>https://api.transmitsecurity.io</string>
84+
<key>clientId</key>
85+
<string>CLIENT_ID</string>
86+
</dict>
87+
</dict>
88+
</plist>
89+
```
90+
#### Android
91+
1. Open your Android manifest XML file, usually located at `android/app/src/main`.
92+
2. Update the strings.xml file in your Application with the following content. The CLIENT_ID should be replaced with your client ID
93+
94+
```xml
95+
<resources>
96+
<!-- Transmit Security Credentials -->
97+
<string name="transmit_security_app_id">"default_application"</string>
98+
<string name="transmit_security_client_id">"CLIENT_ID"</string>
99+
<string name="transmit_security_base_url">https://api.transmitsecurity.io</string>
100+
</resources>
101+
```
102+
71103
```js
72104
import TSAuthenticationSDKModule from 'react-native-ts-authentication';
73105

@@ -77,18 +109,18 @@ componentDidMount(): void {
77109
}
78110

79111
private onAppReady = async (): Promise<void> => {
80-
/* Initialize the module with parameters:
81-
1. ClientID obtained from the application settings in the Transmit portal
82-
2. BaseURL can be "https://api.transmitsecurity.io" | eu = "api.eu.transmitsecurity.io" | ca = "api.ca.transmitsecurity.io"
83-
112+
TSAuthenticationSDKModule.initializeSDK();
113+
114+
/*
115+
Instead of using Plist and strings.xml, you can initialize the module with parameters:
116+
1. ClientID obtained from the application settings in the Transmit portal
117+
2. Custom Domain - Can be null (or undefined if not using BaseURL)
118+
3. BaseURL - Can be null or undefined. "https://api.transmitsecurity.io" | eu = "api.eu.transmitsecurity.io" | ca = "api.ca.transmitsecurity.io"
119+
120+
TSAuthenticationSDKModule.initialize(
121+
"YOUR_CLIENT_ID"
122+
);
84123
*/
85-
const baseURL = "https://api.transmitsecurity.io";
86-
87-
TSAuthenticationSDKModule.initialize(
88-
"YOUR_CLIENT_ID",
89-
"YOUR_DOMAIN",
90-
`${baseURL}/cis/v1`
91-
);
92124
}
93125
```
94126

@@ -128,6 +160,49 @@ onStartSignTransactionProcess = async (): Promise<void> => {
128160
}
129161
```
130162

163+
### Native Biometrics
164+
• For iOS, ensure that you add the necessary permissions to use FaceID in your app's Info.plist file.<br>
165+
• For Android, add the following strings to your app's strings.xml file:
166+
167+
```xml
168+
<resources>
169+
<string name="BiometricPromptTitle">Authenticate with Biometrics</string>
170+
<string name="BiometricPromptSubtitle">Use your device biometrics to authenticate.</string>
171+
<string name="BiometricPromptCancel">Cancel</string>
172+
</resources>
173+
```
174+
175+
#### Register Native Biometrics
176+
```js
177+
onRegisterNativeBiometics = async (username: string): Promise<void> => {
178+
try {
179+
const response = await TSAuthenticationSDKModule.registerNativeBiometrics(username);
180+
// use the response.result string to complete biometrics registration in your backend.
181+
} catch (error) {
182+
console.error(`Error signing a transaction: ${error}`);
183+
}
184+
}
185+
```
186+
187+
#### Authenticate Biometrics
188+
```js
189+
authenticateWithNativeBiometrics = async (username: string): Promise<void> => {
190+
try {
191+
const challenge = this.randomString();
192+
const response = await TSAuthenticationSDKModule.authenticateNativeBiometrics(username, challenge);
193+
// use the response.result string to complete biometrics authentication in your backend.
194+
} catch (error) {
195+
console.error(`Error signing a transaction: ${error}`);
196+
}
197+
}
198+
199+
private randomString = (): string => {
200+
return (Math.random() + 1).toString(36).substring(7);
201+
}
202+
```
203+
204+
### Information about the device
205+
131206
#### Get Device Info
132207
```js
133208
onGetDeviceInfo = async (): Promise<void> => {

RELEASE_NOTES.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
## 0.1.4 July 2024
2+
### Content
3+
#### Enhancements
4+
1. Upgraded native SDKs to iOS `1.1.3` and Android `1.0.19`.
5+
2. Added initializeSDK API to load configuration from `TransmitSecurity.plist` on iOS, and `strings.xml` on Android.
6+
3. Added biometrics authentication API.
7+
4. `uses-sdk:minSdkVersion` Should be equal or greater then `23` to support native biometrics.
8+
5. Added support for custom domain in SDK initialize API.

android/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ dependencies {
8383
// For < 0.71, this will be from the local maven repo
8484
// For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin
8585
//noinspection GradleDynamicVersion
86-
implementation("com.ts.sdk:authentication:1.0.+")
86+
implementation("com.ts.sdk:authentication:1.0.19+")
8787
implementation "com.facebook.react:react-native:+"
8888
}
8989

android/gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
TsAuthentication_kotlinVersion=1.7.0
2-
TsAuthentication_minSdkVersion=21
2+
TsAuthentication_minSdkVersion=23
33
TsAuthentication_targetSdkVersion=31
44
TsAuthentication_compileSdkVersion=31
55
TsAuthentication_ndkversion=21.4.7075529

android/src/main/java/com/tsauthentication/TsAuthenticationModule.java

Lines changed: 126 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
package com.tsauthentication;
22

3+
import android.app.Activity;
4+
import android.content.Context;
5+
36
import androidx.annotation.NonNull;
7+
import androidx.annotation.Nullable;
8+
import androidx.appcompat.app.AppCompatActivity;
9+
410
import com.facebook.react.bridge.Promise;
511
import com.facebook.react.bridge.ReactApplicationContext;
612
import com.facebook.react.bridge.ReactContextBaseJavaModule;
@@ -15,9 +21,15 @@
1521
import com.transmit.authentication.TSAuthCallback;
1622
import com.transmit.authentication.TSAuthentication;
1723
import com.transmit.authentication.TSWebAuthnRegistrationError;
24+
import com.transmit.authentication.biometrics.BiometricPromptTexts;
25+
import com.transmit.authentication.biometrics.TSBiometricsAuthError;
26+
import com.transmit.authentication.biometrics.TSBiometricsAuthResult;
27+
import com.transmit.authentication.biometrics.TSBiometricsRegistrationError;
28+
import com.transmit.authentication.biometrics.TSBiometricsRegistrationResult;
1829
import com.transmit.authentication.network.completereg.DeviceInfo;
1930

2031
import java.util.HashMap;
32+
import java.util.Map;
2133

2234
@ReactModule(name = TsAuthenticationModule.NAME)
2335
public class TsAuthenticationModule extends ReactContextBaseJavaModule {
@@ -39,10 +51,22 @@ public String getName() {
3951
@NonNull public void initialize(String clientId, String domain, String baseUrl, Promise promise) {
4052

4153
if(reactContext.getCurrentActivity() != null) {
42-
TSAuthentication.initialize(
43-
reactContext,
44-
clientId
45-
);
54+
55+
if (domain.length() > 0) {
56+
TSAuthentication.initialize(
57+
reactContext,
58+
clientId,
59+
baseUrl,
60+
domain
61+
);
62+
} else {
63+
TSAuthentication.initialize(
64+
reactContext,
65+
clientId,
66+
baseUrl,
67+
null
68+
);
69+
}
4670
promise.resolve(true);
4771
}
4872
}
@@ -81,7 +105,7 @@ public void success(RegistrationResult registrationResult) {
81105
}
82106
@Override
83107
public void error(TSWebAuthnRegistrationError tsWebAuthnRegistrationError) {
84-
promise.reject("result", tsWebAuthnRegistrationError.getEM());
108+
promise.reject("result", tsWebAuthnRegistrationError.getErrorMessage());
85109
}
86110
}
87111
);
@@ -134,6 +158,103 @@ public void error(TSWebAuthnAuthenticationError tsWebAuthnAuthenticationError) {
134158
}
135159
}
136160

161+
// Native Biometrics
162+
163+
@ReactMethod
164+
@NonNull public void registerNativeBiometrics(String username, Promise promise) {
165+
if(reactContext.getCurrentActivity() != null) {
166+
TSAuthentication.registerNativeBiometrics(
167+
reactContext.getCurrentActivity(),
168+
username,
169+
new TSAuthCallback<TSBiometricsRegistrationResult, TSBiometricsRegistrationError>() {
170+
@Override
171+
public void success(TSBiometricsRegistrationResult tsBiometricsRegistrationResult) {
172+
WritableMap map = new WritableNativeMap();
173+
map.putString("publicKeyId", tsBiometricsRegistrationResult.keyId());
174+
map.putString("publicKey", tsBiometricsRegistrationResult.publicKey());
175+
map.putString("os", "Android");
176+
promise.resolve(map);
177+
}
178+
179+
@Override
180+
public void error(TSBiometricsRegistrationError tsBiometricsRegistrationError) {
181+
promise.reject("result", tsBiometricsRegistrationError.toString());
182+
}
183+
}
184+
);
185+
}
186+
}
187+
188+
@ReactMethod
189+
@NonNull public void authenticateNativeBiometrics(String username, String challenge, Promise promise) {
190+
if(reactContext.getCurrentActivity() != null) {
191+
192+
AppCompatActivity appCompatActivity = getAppCompatActivity();
193+
if (appCompatActivity == null) {
194+
promise.reject("result", "current activity is not an instance of AppCompatActivity");
195+
return;
196+
}
197+
198+
Map<String, String> biometricsString = getBiometricsStrings();
199+
BiometricPromptTexts promptTexts = new BiometricPromptTexts(
200+
biometricsString.get("titleTxt"),
201+
biometricsString.get("subtitleTxt"),
202+
biometricsString.get("cancelTxt")
203+
);
204+
205+
TSAuthentication.authenticateNativeBiometrics(
206+
appCompatActivity,
207+
username,
208+
challenge,
209+
promptTexts,
210+
new TSAuthCallback<TSBiometricsAuthResult, TSBiometricsAuthError>() {
211+
@Override
212+
public void success(TSBiometricsAuthResult tsBiometricsAuthResult) {
213+
WritableMap map = new WritableNativeMap();
214+
map.putString("publicKeyId", tsBiometricsAuthResult.keyId());
215+
map.putString("signature", tsBiometricsAuthResult.signature());
216+
promise.resolve(map);
217+
}
218+
219+
@Override
220+
public void error(TSBiometricsAuthError tsBiometricsAuthError) {
221+
promise.reject("result", tsBiometricsAuthError.toString());
222+
}
223+
}
224+
);
225+
}
226+
}
227+
228+
@Nullable
229+
private AppCompatActivity getAppCompatActivity() {
230+
Activity activity = reactContext.getCurrentActivity();
231+
if (activity instanceof AppCompatActivity) {
232+
return (AppCompatActivity) activity;
233+
} else {
234+
return null;
235+
}
236+
}
237+
238+
private Map<String, String> getBiometricsStrings() {
239+
Context context = reactContext;
240+
241+
String titleTxt = getStringResourceByName(context, "BiometricPromptTitle", "Authenticate with Biometrics");
242+
String subtitleTxt = getStringResourceByName(context, "BiometricPromptSubtitle", "Use your device biometrics to authenticate.");
243+
String cancelTxt = getStringResourceByName(context, "BiometricPromptCancel", "Cancel");
244+
245+
Map<String, String> biometricsStrings = new HashMap<>();
246+
biometricsStrings.put("titleTxt", titleTxt);
247+
biometricsStrings.put("subtitleTxt", subtitleTxt);
248+
biometricsStrings.put("cancelTxt", cancelTxt);
249+
250+
return biometricsStrings;
251+
}
252+
253+
private String getStringResourceByName(Context context, String resourceName, String defaultValue) {
254+
int resId = context.getResources().getIdentifier(resourceName, "string", context.getPackageName());
255+
return resId != 0 ? context.getString(resId) : defaultValue;
256+
}
257+
137258
@ReactMethod
138259
@NonNull public void getDeviceInfo(Promise promise) {
139260
if(reactContext.getCurrentActivity() != null) {

example/android/app/src/main/res/values/strings.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,7 @@
55
\"include\": \"https://mobile.idsec-dev.com/.well-known/assetlinks.json\"
66
}]
77
</string>
8+
<string name="BiometricPromptTitle">Authenticate with Biometrics</string>
9+
<string name="BiometricPromptSubtitle">Use your device biometrics to authenticate.</string>
10+
<string name="BiometricPromptCancel">Cancel</string>
811
</resources>

example/android/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
buildscript {
44
ext {
55
buildToolsVersion = "34.0.0"
6-
minSdkVersion = 21
6+
minSdkVersion = 23
77
compileSdkVersion = 34
88
targetSdkVersion = 34
99
kotlin_version = "1.9.20"

0 commit comments

Comments
 (0)