react-native-rdservice 0.1.0 β†’ 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,58 +1,112 @@
1
1
  # react-native-rdservice
2
2
 
3
- A React Native library for capturing biometric data (fingerprint and face) using RD (Registered Device) services on Android. This library integrates with UIDAI-compliant biometric devices for Aadhaar-based authentication and eKYC services.
3
+ A React Native library for capturing **fingerprint**, **iris**, and **face** biometric data using UIDAI-compliant RD (Registered Device) services on Android. Built for Aadhaar-based authentication and eKYC workflows.
4
4
 
5
- ## Installation
5
+ [![npm version](https://img.shields.io/npm/v/react-native-rdservice.svg)](https://www.npmjs.com/package/react-native-rdservice)
6
+ [![license](https://img.shields.io/npm/l/react-native-rdservice.svg)](LICENSE)
6
7
 
7
- ```sh
8
- npm install react-native-rdservice
9
- ```
8
+ ## Features
9
+
10
+ - πŸ«† Fingerprint capture via RD services (Mantra, Morpho, Startek, Precision, SecuGen, …)
11
+ - πŸ‘οΈ Iris capture via iris RD services
12
+ - πŸ™‚ Face capture via UIDAI Face RD
13
+ - ℹ️ Device readiness check (`getDeviceInfo`)
14
+ - Custom `PidOptions` XML support with sensible defaults
15
+ - Promise-based API with full TypeScript definitions
16
+ - New Architecture (TurboModule) β€” works with React Native 0.80+
17
+ - Android 11+ package visibility (`<queries>`) handled automatically β€” no manifest changes needed in your app
10
18
 
11
- or
19
+ ## Platform support
20
+
21
+ | Platform | Supported | Notes |
22
+ |----------|-----------|-------|
23
+ | Android | βœ… | Requires an RD service app installed on the device |
24
+ | iOS | ⚠️ Graceful stub | UIDAI certifies RD services for **Android and Windows only**. There are no iOS RD service apps, so real capture is impossible on iOS. The module still loads on iOS and every method resolves with `{ status: 'FAILURE', message: '…not available on iOS…' }`, so cross-platform apps can share one code path without crashing. |
25
+
26
+ ## Requirements
27
+
28
+ - React Native **0.80 or higher** with the New Architecture enabled (default since RN 0.76)
29
+ - Android SDK 24+
30
+ - The relevant RD service app installed on the device (see [package names](#common-rd-service-package-names))
31
+
32
+ ## Installation
12
33
 
13
34
  ```sh
35
+ npm install react-native-rdservice
36
+ # or
14
37
  yarn add react-native-rdservice
15
38
  ```
16
39
 
17
- ## Features
40
+ No extra Android setup is required. The library ships the required `<queries>` entries for Android 11+ package visibility; they are merged into your app's manifest automatically.
18
41
 
19
- - Fingerprint biometric capture using RD services
20
- - Face biometric capture using RD services
21
- - Support for custom PID options
22
- - TypeScript support with full type definitions
23
- - Promise-based API for easy async/await usage
42
+ ## Quick start
24
43
 
25
- ## API Reference
44
+ ```typescript
45
+ import { getFingerPrint } from 'react-native-rdservice';
46
+
47
+ const result = await getFingerPrint('com.mantra.rdservice');
26
48
 
27
- ### Types
49
+ if (result.status === 'SUCCESS') {
50
+ // result.message is the signed PidData XML β€” send it to your backend as-is
51
+ } else {
52
+ console.warn(result.message); // e.g. "Device not ready"
53
+ }
54
+ ```
28
55
 
29
- #### `RdServiceResponse`
56
+ ## API Reference
30
57
 
31
- Response object returned by both fingerprint and face capture methods.
58
+ All methods return `Promise<RdServiceResponse>`:
32
59
 
33
60
  ```typescript
34
61
  interface RdServiceResponse {
35
- status: string; // "SUCCESS" or "FAILURE"
36
- message: string; // XML data on success, error message on failure
62
+ status: string; // 'SUCCESS' | 'FAILURE'
63
+ message: string; // signed XML on success, human-readable error on failure
37
64
  }
38
65
  ```
39
66
 
40
- ### Methods
67
+ `deviceName` is always the **package name of the RD service app** installed on the device. `pidOption` is an optional `PidOptions` XML string β€” when omitted (or blank), a sensible default is used.
68
+
69
+ ---
70
+
71
+ ### `getDeviceInfo(deviceName)`
72
+
73
+ Checks whether the RD service and its device are ready (RD `INFO` call).
74
+
75
+ ```typescript
76
+ import { getDeviceInfo } from 'react-native-rdservice';
77
+
78
+ const info = await getDeviceInfo('com.mantra.rdservice');
79
+ if (info.status === 'SUCCESS') {
80
+ // info.message contains the RDService info XML (device status, dpId, rdsId, …)
81
+ }
82
+ ```
41
83
 
42
84
  ---
43
85
 
44
- #### `getFingerPrint(deviceName, pidOption)`
86
+ ### `getFingerPrint(deviceName, pidOption?)`
45
87
 
46
- Captures fingerprint biometric data using the specified RD service device.
88
+ Captures fingerprint biometric data.
47
89
 
48
- **Parameters:**
90
+ ```typescript
91
+ import { getFingerPrint } from 'react-native-rdservice';
92
+
93
+ // Default options: single finger (fCount="1"), FMR format, 10s timeout, production env
94
+ const result = await getFingerPrint('com.mantra.rdservice');
95
+
96
+ // Custom PidOptions
97
+ const custom = await getFingerPrint(
98
+ 'com.precision.pb510.rdservice',
99
+ `<?xml version="1.0"?>
100
+ <PidOptions ver="1.0">
101
+ <Opts fCount="2" fType="2" iCount="0" pCount="0" format="0"
102
+ pidVer="2.0" timeout="15000" posh="UNKNOWN" env="P" />
103
+ <CustOpts><Param name="txnId" value="12345"/></CustOpts>
104
+ </PidOptions>`
105
+ );
106
+ ```
49
107
 
50
- | Parameter | Type | Required | Description |
51
- |-----------|------|----------|-------------|
52
- | `deviceName` | `string` | Yes | Package name of the RD service app (e.g., `"com.mantra.rdservice"`, `"com.precision.pb510.rdservice"`) |
53
- | `pidOption` | `string` | No | XML string containing PID options. If empty or less than 10 characters, uses default configuration |
108
+ Default `PidOptions`:
54
109
 
55
- **Default PID Options:**
56
110
  ```xml
57
111
  <?xml version="1.0"?>
58
112
  <PidOptions ver="1.0">
@@ -62,80 +116,48 @@ Captures fingerprint biometric data using the specified RD service device.
62
116
  </PidOptions>
63
117
  ```
64
118
 
65
- **Returns:** `Promise<RdServiceResponse>`
119
+ ---
66
120
 
67
- **Success Response:**
68
- ```typescript
69
- {
70
- status: "SUCCESS",
71
- message: "<?xml version='1.0'?><PidData>...</PidData>" // Biometric XML data
72
- }
73
- ```
121
+ ### `getIrisCapture(deviceName, pidOption?)`
122
+
123
+ Captures iris biometric data.
74
124
 
75
- **Error Response:**
76
125
  ```typescript
77
- {
78
- status: "FAILURE",
79
- message: "Device not ready" | "Selected device not found" | "RD services not available"
126
+ import { getIrisCapture } from 'react-native-rdservice';
127
+
128
+ // Default options: single iris (iCount="1"), 10s timeout, production env
129
+ const result = await getIrisCapture('com.mantra.mis100v2.rdservice');
130
+
131
+ if (result.status === 'SUCCESS') {
132
+ // result.message contains the signed PidData XML with iris data
80
133
  }
81
134
  ```
82
135
 
83
- **Example:**
84
-
85
- ```typescript
86
- import { getFingerPrint } from 'react-native-rdservice';
136
+ Default `PidOptions`:
87
137
 
88
- // Using default PID options
89
- const captureFingerprint = async () => {
90
- try {
91
- const result = await getFingerPrint('com.mantra.rdservice', '');
92
-
93
- if (result.status === 'SUCCESS') {
94
- console.log('Fingerprint captured:', result.message);
95
- // Process the biometric XML data
96
- // result.message contains the PID XML with biometric information
97
- } else {
98
- console.error('Capture failed:', result.message);
99
- }
100
- } catch (error) {
101
- console.error('Error:', error);
102
- }
103
- };
104
-
105
- // Using custom PID options
106
- const captureFingerprintCustom = async () => {
107
- const customPidOptions = `<?xml version="1.0"?>
108
- <PidOptions ver="1.0">
109
- <Opts fCount="2" fType="2" iCount="0" pCount="0" format="0"
110
- pidVer="2.0" timeout="15000" posh="UNKNOWN" env="P" />
111
- <CustOpts>
112
- <Param name="txnId" value="12345"/>
113
- </CustOpts>
114
- </PidOptions>`;
115
-
116
- const result = await getFingerPrint('com.precision.pb510.rdservice', customPidOptions);
117
-
118
- if (result.status === 'SUCCESS') {
119
- // Handle success
120
- console.log('Biometric data:', result.message);
121
- }
122
- };
138
+ ```xml
139
+ <?xml version="1.0"?>
140
+ <PidOptions ver="1.0">
141
+ <Opts fCount="0" fType="0" iCount="1" iType="0" pCount="0" pType="0"
142
+ format="0" pidVer="2.0" timeout="10000" posh="UNKNOWN" env="P" />
143
+ <CustOpts></CustOpts>
144
+ </PidOptions>
123
145
  ```
124
146
 
125
147
  ---
126
148
 
127
- #### `getFaceCapture(deviceName, pidOption)`
149
+ ### `getFaceCapture(deviceName, pidOption?)`
150
+
151
+ Captures face biometric data via the UIDAI Face RD app.
128
152
 
129
- Captures face biometric data using the specified RD service device.
153
+ ```typescript
154
+ import { getFaceCapture } from 'react-native-rdservice';
130
155
 
131
- **Parameters:**
156
+ const result = await getFaceCapture('in.gov.uidai.facerd');
157
+ ```
132
158
 
133
- | Parameter | Type | Required | Description |
134
- |-----------|------|----------|-------------|
135
- | `deviceName` | `string` | Yes | Package name of the face RD service app (e.g., `"in.gov.uidai.facerd"`) |
136
- | `pidOption` | `string` | No | XML string containing PID options for face capture. If empty or less than 10 characters, uses default configuration |
159
+ The default `PidOptions` targets **face authentication** and uses the UIDAI-published `wadh` constant for that flow. For eKYC or other purposes, pass your own `PidOptions` with the appropriate `wadh` and `purpose`:
137
160
 
138
- **Default PID Options:**
139
161
  ```xml
140
162
  <?xml version="1.0" encoding="UTF-8"?>
141
163
  <PidOptions ver="1.0" env="P">
@@ -150,170 +172,129 @@ Captures face biometric data using the specified RD service device.
150
172
  </PidOptions>
151
173
  ```
152
174
 
153
- **Returns:** `Promise<RdServiceResponse>`
154
-
155
- **Success Response:**
156
- ```typescript
157
- {
158
- status: "SUCCESS",
159
- message: "<?xml version='1.0'?><PidData>...</PidData>" // Face biometric XML data
160
- }
161
- ```
162
-
163
- **Error Response:**
164
- ```typescript
165
- {
166
- status: "FAILURE",
167
- message: "Device not ready" | "Selected device not found" | "Face RD services not available"
168
- }
169
- ```
170
-
171
- **Example:**
172
-
173
- ```typescript
174
- import { getFaceCapture } from 'react-native-rdservice';
175
-
176
- // Using default PID options
177
- const captureFace = async () => {
178
- try {
179
- const result = await getFaceCapture('in.gov.uidai.facerd', '');
180
-
181
- if (result.status === 'SUCCESS') {
182
- console.log('Face captured:', result.message);
183
- // Process the face biometric XML data
184
- // result.message contains the PID XML with face biometric information
185
- } else {
186
- console.error('Capture failed:', result.message);
187
- }
188
- } catch (error) {
189
- console.error('Error:', error);
190
- }
191
- };
192
-
193
- // Using custom PID options
194
- const captureFaceCustom = async () => {
195
- const customPidOptions = `<?xml version="1.0" encoding="UTF-8"?>
196
- <PidOptions ver="1.0" env="P">
197
- <Opts fCount="" fType="" iCount="" iType="" pCount="" pType=""
198
- format="" pidVer="2.0" timeout="20000" otp=""
199
- wadh="your_wadh_value_here" posh="" />
200
- <CustOpts>
201
- <Param name="txnId" value="customTxnId123"/>
202
- <Param name="purpose" value="ekyc"/>
203
- <Param name="language" value="en"/>
204
- </CustOpts>
205
- </PidOptions>`;
206
-
207
- const result = await getFaceCapture('in.gov.uidai.facerd', customPidOptions);
208
-
209
- if (result.status === 'SUCCESS') {
210
- // Handle success
211
- console.log('Face biometric data:', result.message);
212
- }
213
- };
214
- ```
215
-
216
175
  ---
217
176
 
218
- ## Complete Usage Example
177
+ ## Complete usage example
219
178
 
220
179
  ```typescript
221
180
  import React, { useState } from 'react';
222
- import { View, Button, Text, Alert } from 'react-native';
223
- import { getFingerPrint, getFaceCapture, type RdServiceResponse } from 'react-native-rdservice';
224
-
225
- const BiometricCapture = () => {
226
- const [fpData, setFpData] = useState<string>('');
227
- const [faceData, setFaceData] = useState<string>('');
228
-
229
- const handleFingerprintCapture = async () => {
181
+ import { View, Button, Alert } from 'react-native';
182
+ import {
183
+ getDeviceInfo,
184
+ getFingerPrint,
185
+ getIrisCapture,
186
+ getFaceCapture,
187
+ type RdServiceResponse,
188
+ } from 'react-native-rdservice';
189
+
190
+ export default function BiometricCapture() {
191
+ const [busy, setBusy] = useState(false);
192
+
193
+ const capture = async (fn: () => Promise<RdServiceResponse>) => {
194
+ setBusy(true);
230
195
  try {
231
- const result: RdServiceResponse = await getFingerPrint(
232
- 'com.mantra.rdservice',
233
- ''
234
- );
235
-
236
- if (result.status === 'SUCCESS') {
237
- setFpData(result.message);
238
- Alert.alert('Success', 'Fingerprint captured successfully');
239
- } else {
240
- Alert.alert('Error', result.message);
241
- }
242
- } catch (error) {
243
- Alert.alert('Error', 'Failed to capture fingerprint');
244
- console.error(error);
245
- }
246
- };
247
-
248
- const handleFaceCapture = async () => {
249
- try {
250
- const result: RdServiceResponse = await getFaceCapture(
251
- 'in.gov.uidai.facerd',
252
- ''
253
- );
254
-
196
+ const result = await fn();
255
197
  if (result.status === 'SUCCESS') {
256
- setFaceData(result.message);
257
- Alert.alert('Success', 'Face captured successfully');
198
+ // Forward result.message (signed PID XML) to your backend over TLS.
199
+ Alert.alert('Success', 'Biometric captured');
258
200
  } else {
259
- Alert.alert('Error', result.message);
201
+ Alert.alert('Failed', result.message);
260
202
  }
261
- } catch (error) {
262
- Alert.alert('Error', 'Failed to capture face');
263
- console.error(error);
203
+ } finally {
204
+ setBusy(false);
264
205
  }
265
206
  };
266
207
 
267
208
  return (
268
209
  <View>
269
- <Button title="Capture Fingerprint" onPress={handleFingerprintCapture} />
270
- <Button title="Capture Face" onPress={handleFaceCapture} />
271
-
272
- {fpData ? <Text>Fingerprint Data: {fpData.substring(0, 100)}...</Text> : null}
273
- {faceData ? <Text>Face Data: {faceData.substring(0, 100)}...</Text> : null}
210
+ <Button
211
+ title="Check Device"
212
+ disabled={busy}
213
+ onPress={() => capture(() => getDeviceInfo('com.mantra.rdservice'))}
214
+ />
215
+ <Button
216
+ title="Fingerprint"
217
+ disabled={busy}
218
+ onPress={() => capture(() => getFingerPrint('com.mantra.rdservice'))}
219
+ />
220
+ <Button
221
+ title="Iris"
222
+ disabled={busy}
223
+ onPress={() => capture(() => getIrisCapture('com.mantra.mis100v2.rdservice'))}
224
+ />
225
+ <Button
226
+ title="Face"
227
+ disabled={busy}
228
+ onPress={() => capture(() => getFaceCapture('in.gov.uidai.facerd'))}
229
+ />
274
230
  </View>
275
231
  );
276
- };
277
-
278
- export default BiometricCapture;
232
+ }
279
233
  ```
280
234
 
281
- ## Common RD Service Package Names
235
+ ## Common RD service package names
282
236
 
283
- **Fingerprint Devices:**
284
- - Mantra: `com.mantra.rdservice`
285
- - Morpho: `com.scl.rdservice`
286
- - Precision: `com.precision.pb510.rdservice`
287
- - Startek: `com.acpl.registersdk`
288
- - Secugen: `com.secugen.rdservice`
237
+ **Fingerprint:**
289
238
 
290
- **Face Devices:**
291
- - UIDAI Face RD: `in.gov.uidai.facerd`
239
+ | Vendor | Package |
240
+ |--------|---------|
241
+ | Mantra MFS100 | `com.mantra.rdservice` |
242
+ | Morpho | `com.scl.rdservice` |
243
+ | Precision PB510 | `com.precision.pb510.rdservice` |
244
+ | Startek | `com.acpl.registersdk` |
245
+ | SecuGen | `com.secugen.rdservice` |
246
+ | Tatvik | `com.tatvik.bio.tmf20` |
292
247
 
293
- ## Requirements
248
+ **Iris:**
294
249
 
295
- - React Native 0.60 or higher
296
- - Android SDK 24 or higher
297
- - RD service app installed on the device
250
+ | Vendor | Package |
251
+ |--------|---------|
252
+ | Mantra MIS100V2 | `com.mantra.mis100v2.rdservice` |
253
+ | IriTech IriShield | `com.iritech.rdservice` |
298
254
 
299
- ## Platform Support
255
+ **Face:**
300
256
 
301
- - Android: βœ… Supported
302
- - iOS: ❌ Not supported (RD services are Android-only)
257
+ | Vendor | Package |
258
+ |--------|---------|
259
+ | UIDAI Face RD | `in.gov.uidai.facerd` |
303
260
 
304
- ## Error Handling
261
+ > Package names can change between vendor releases β€” verify against the RD service app actually installed on your target devices.
305
262
 
306
- The library returns descriptive error messages in the `message` field when `status` is `"FAILURE"`:
263
+ ## Error handling
307
264
 
308
- - **"Device not ready"**: The biometric device is not connected or not ready
309
- - **"Selected device not found"**: The specified RD service package is not installed
310
- - **"RD services not available"**: The RD service app is not available on the device
311
- - **"No action taken"**: User cancelled the operation
265
+ Failures resolve (never reject) with `status: 'FAILURE'` and one of:
312
266
 
313
- ## License
267
+ | Message | Meaning |
268
+ |---------|---------|
269
+ | `Device not ready` | The biometric device is not connected, not initialised, or returned an invalid response |
270
+ | `Selected device not found` | No app with the given package name handles the RD capture intent |
271
+ | `No action taken` | The user cancelled the capture |
272
+ | `Invalid RD service package name` | `deviceName` is not a valid Android package name |
273
+ | `Another RD service request is already in progress` | A previous capture has not finished yet |
274
+ | `No foreground activity available to start RD service` | The app is not in the foreground |
275
+ | `UIDAI RD services are not available on iOS…` | Called on iOS, where RD services do not exist |
314
276
 
315
- MIT
277
+ ## Security notes
278
+
279
+ Biometric data is highly sensitive. When integrating this library:
280
+
281
+ - **Never modify the PID XML.** The `message` returned on success is digitally signed by the RD service; altering a single character breaks UIDAI signature verification. This library returns it byte-for-byte as produced.
282
+ - **Never log or persist raw PID data** on the device. Forward it to your backend over TLS and discard it.
283
+ - **The PID block is already encrypted** by the RD service (per UIDAI spec) β€” your app cannot and should not decrypt it; only the authorised backend/AUA can process it.
284
+ - **Validate the RD service app.** The library only launches explicit intents against a syntactically valid package name, so the capture request cannot be hijacked by an arbitrary app β€” but you should still pin the exact vendor package(s) you support.
285
+ - **Use `env="PP"` (pre-production) PidOptions during development** and `env="P"` only in production.
286
+ - Comply with the UIDAI Aadhaar Act and data-protection regulations applicable to your deployment.
287
+
288
+ ## Troubleshooting
289
+
290
+ - **`Selected device not found` on Android 11+**: fixed by this library's built-in `<queries>` declarations β€” rebuild the app after installing/updating the library.
291
+ - **Capture never returns**: ensure the RD service app is installed, registered, and the device is plugged in; call `getDeviceInfo` first to check readiness.
292
+ - **Works in debug but not release**: confirm your ProGuard/R8 rules don't strip the RD service vendor SDK (this library itself needs no extra rules).
316
293
 
317
294
  ## Contributing
318
295
 
319
- See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository.
296
+ See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow.
297
+
298
+ ## License
299
+
300
+ MIT
@@ -74,6 +74,8 @@ repositories {
74
74
  def kotlin_version = getExtOrDefault("kotlinVersion")
75
75
 
76
76
  dependencies {
77
- implementation 'com.facebook.react:react-android:0.83.0'
77
+ // Version is resolved by the consumer app's React Native Gradle plugin, so the
78
+ // library works with any React Native version (0.80+) instead of pinning one.
79
+ implementation "com.facebook.react:react-android"
78
80
  implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
79
81
  }
@@ -1,5 +1,5 @@
1
1
  Rdservice_kotlinVersion=2.0.21
2
2
  Rdservice_minSdkVersion=24
3
- Rdservice_targetSdkVersion=34
3
+ Rdservice_targetSdkVersion=35
4
4
  Rdservice_compileSdkVersion=35
5
5
  Rdservice_ndkVersion=27.1.12297006
@@ -1,2 +1,21 @@
1
1
  <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+ <!--
3
+ Android 11+ package visibility: without these entries the host app cannot
4
+ see or launch the RD service apps installed on the device. Merged into the
5
+ consumer app's manifest automatically.
6
+ -->
7
+ <queries>
8
+ <intent>
9
+ <action android:name="in.gov.uidai.rdservice.fp.INFO" />
10
+ </intent>
11
+ <intent>
12
+ <action android:name="in.gov.uidai.rdservice.fp.CAPTURE" />
13
+ </intent>
14
+ <intent>
15
+ <action android:name="in.gov.uidai.rdservice.iris.CAPTURE" />
16
+ </intent>
17
+ <intent>
18
+ <action android:name="in.gov.uidai.rdservice.face.CAPTURE" />
19
+ </intent>
20
+ </queries>
2
21
  </manifest>
@@ -2,6 +2,7 @@ package com.rdservice
2
2
 
3
3
  import android.app.Activity
4
4
  import android.content.Intent
5
+ import android.util.Log
5
6
  import com.facebook.react.bridge.ActivityEventListener
6
7
  import com.facebook.react.bridge.Arguments
7
8
  import com.facebook.react.bridge.BaseActivityEventListener
@@ -16,72 +17,69 @@ class RdserviceModule(reactContext: ReactApplicationContext) :
16
17
 
17
18
  companion object {
18
19
  const val NAME = "Rdservice"
19
- const val RDINFO_CODE = 1
20
- const val RDCAPTURE_CODE = 2
21
- const val FACE_CAPTURE_CODE = 9
20
+
21
+ private const val RDINFO_CODE = 1
22
+ private const val RDCAPTURE_CODE = 2
23
+ private const val IRIS_CAPTURE_CODE = 3
24
+ private const val FACE_CAPTURE_CODE = 9
25
+
26
+ private const val ACTION_FP_INFO = "in.gov.uidai.rdservice.fp.INFO"
27
+ private const val ACTION_FP_CAPTURE = "in.gov.uidai.rdservice.fp.CAPTURE"
28
+ private const val ACTION_IRIS_CAPTURE = "in.gov.uidai.rdservice.iris.CAPTURE"
29
+ private const val ACTION_FACE_CAPTURE = "in.gov.uidai.rdservice.face.CAPTURE"
30
+
31
+ private const val SUCCESS = "SUCCESS"
32
+ private const val FAILURE = "FAILURE"
33
+
34
+ // RD service responses shorter than this cannot contain valid XML.
35
+ private const val MIN_VALID_RESPONSE_LENGTH = 10
36
+
37
+ // Valid Android application id, e.g. com.mantra.rdservice. Rejecting anything
38
+ // else prevents the capture intent from being sent as an implicit broadcast
39
+ // that an arbitrary app could answer.
40
+ private val PACKAGE_NAME_PATTERN =
41
+ Regex("^[a-zA-Z][a-zA-Z0-9_]*(\\.[a-zA-Z][a-zA-Z0-9_]*)+$")
42
+
43
+ private const val DEFAULT_FINGER_PID_OPTIONS =
44
+ """<?xml version="1.0"?><PidOptions ver="1.0"><Opts fCount="1" fType="2" iCount="0" pCount="0" format="0" pidVer="2.0" timeout="10000" posh="UNKNOWN" env="P" /><CustOpts></CustOpts></PidOptions>"""
45
+
46
+ private const val DEFAULT_IRIS_PID_OPTIONS =
47
+ """<?xml version="1.0"?><PidOptions ver="1.0"><Opts fCount="0" fType="0" iCount="1" iType="0" pCount="0" pType="0" format="0" pidVer="2.0" timeout="10000" posh="UNKNOWN" env="P" /><CustOpts></CustOpts></PidOptions>"""
48
+
49
+ // The wadh value below is the UIDAI-published constant for face authentication.
50
+ // Pass custom PidOptions for eKYC or other flows.
51
+ private const val DEFAULT_FACE_PID_OPTIONS =
52
+ """<?xml version="1.0" encoding="UTF-8"?> <PidOptions ver="1.0" env="P"><Opts fCount="" fType="" iCount="" iType="" pCount="" pType="" format="" pidVer="2.0" timeout="" otp="" wadh="sgydIC09zzy6f8Lb3xaAqzKquKe9lFcNR9uTvYxFp+A=" posh="" /> <CustOpts><Param name="txnId" value="76435891"/><Param name="purpose" value="auth"/><Param name="language" value="en"/></CustOpts></PidOptions>"""
22
53
  }
23
54
 
24
- private val SUCCESS = "SUCCESS"
25
- private val FAILURE = "FAILURE"
26
- private var pckName = ""
27
- private var pidOption = ""
28
55
  private var promise: Promise? = null
29
56
 
30
57
  private val activityEventListener: ActivityEventListener = object : BaseActivityEventListener() {
31
- override fun onActivityResult(activity: Activity?, requestCode: Int, resultCode: Int, data: Intent?) {
58
+ override fun onActivityResult(
59
+ activity: Activity,
60
+ requestCode: Int,
61
+ resultCode: Int,
62
+ data: Intent?,
63
+ ) {
64
+ when (requestCode) {
65
+ RDINFO_CODE,
66
+ RDCAPTURE_CODE,
67
+ IRIS_CAPTURE_CODE,
68
+ FACE_CAPTURE_CODE,
69
+ -> Unit
70
+ // Results from unrelated activities must not touch a pending capture.
71
+ else -> return
72
+ }
73
+
32
74
  if (data == null) {
33
75
  resolve(FAILURE, "No action taken")
34
76
  return
35
77
  }
36
78
 
37
79
  when (requestCode) {
38
- RDINFO_CODE -> {
39
- val requiredValue = data.getStringExtra("RD_SERVICE_INFO")
40
-
41
- if (requiredValue == null) {
42
- resolve(FAILURE, "Device not ready")
43
- return
44
- }
45
- if (requiredValue.length <= 10) {
46
- resolve(FAILURE, "Device not ready")
47
- return
48
- }
49
- if (requiredValue.lowercase().contains("notready")) {
50
- resolve(FAILURE, "Device not ready")
51
- return
52
- }
53
-
54
- captureData()
55
- return
56
- }
57
-
58
- RDCAPTURE_CODE -> {
59
- val captureXML = data.getStringExtra("PID_DATA")
60
-
61
- if (captureXML == null || captureXML.length <= 10) {
62
- resolve(FAILURE, "Device not ready")
63
- return
64
- }
65
- if (captureXML.lowercase().contains("device not ready")) {
66
- resolve(FAILURE, "Device not ready")
67
- return
68
- }
69
- resolve(SUCCESS, captureXML)
70
- }
71
-
72
- FACE_CAPTURE_CODE -> {
73
- val captureXML = data.getStringExtra("response")
74
-
75
- if (captureXML == null || captureXML.length <= 10) {
76
- resolve(FAILURE, "Device not ready")
77
- return
78
- }
79
- if (captureXML.lowercase().contains("device not ready")) {
80
- resolve(FAILURE, "Device not ready")
81
- return
82
- }
83
- resolve(SUCCESS, captureXML)
84
- }
80
+ RDINFO_CODE -> handleInfoResult(data.getStringExtra("RD_SERVICE_INFO"))
81
+ RDCAPTURE_CODE, IRIS_CAPTURE_CODE -> handleCaptureResult(data.getStringExtra("PID_DATA"))
82
+ FACE_CAPTURE_CODE -> handleCaptureResult(data.getStringExtra("response"))
85
83
  }
86
84
  }
87
85
  }
@@ -90,95 +88,109 @@ class RdserviceModule(reactContext: ReactApplicationContext) :
90
88
  reactContext.addActivityEventListener(activityEventListener)
91
89
  }
92
90
 
93
- override fun getName(): String {
94
- return NAME
95
- }
91
+ override fun getName(): String = NAME
96
92
 
97
- private fun captureData() {
98
- var finalPidOption = """<?xml version="1.0"?><PidOptions ver="1.0"><Opts fCount="1" fType="2" iCount="0" pCount="0" format="0" pidVer="2.0" timeout="10000" posh="UNKNOWN" env="P" /><CustOpts></CustOpts></PidOptions>"""
93
+ override fun invalidate() {
94
+ reactApplicationContext.removeActivityEventListener(activityEventListener)
95
+ promise = null
96
+ super.invalidate()
97
+ }
99
98
 
100
- if (pidOption.length >= 10) {
101
- finalPidOption = pidOption
99
+ override fun getDeviceInfo(deviceName: String, promise: Promise) {
100
+ startRdActivity(promise, deviceName, RDINFO_CODE) {
101
+ Intent(ACTION_FP_INFO)
102
102
  }
103
+ }
103
104
 
104
- val intent = Intent().apply {
105
- action = "in.gov.uidai.rdservice.fp.CAPTURE"
106
- putExtra("PID_OPTIONS", finalPidOption)
107
- setPackage(pckName)
105
+ override fun getFingerPrint(deviceName: String, pidOption: String, promise: Promise) {
106
+ startRdActivity(promise, deviceName, RDCAPTURE_CODE) {
107
+ Intent(ACTION_FP_CAPTURE)
108
+ .putExtra("PID_OPTIONS", pidOptionOrDefault(pidOption, DEFAULT_FINGER_PID_OPTIONS))
108
109
  }
110
+ }
109
111
 
110
- val currentActivity = currentActivity
111
- try {
112
- currentActivity?.startActivityForResult(intent, RDCAPTURE_CODE)
113
- } catch (e: Exception) {
114
- e.printStackTrace()
115
- resolve(FAILURE, "Selected device not found")
112
+ override fun getIrisCapture(deviceName: String, pidOption: String, promise: Promise) {
113
+ startRdActivity(promise, deviceName, IRIS_CAPTURE_CODE) {
114
+ Intent(ACTION_IRIS_CAPTURE)
115
+ .putExtra("PID_OPTIONS", pidOptionOrDefault(pidOption, DEFAULT_IRIS_PID_OPTIONS))
116
116
  }
117
117
  }
118
118
 
119
- private fun captureFaceData() {
120
- var finalPidOption = """<?xml version="1.0" encoding="UTF-8"?> <PidOptions ver="1.0" env="P"><Opts fCount="" fType="" iCount="" iType="" pCount="" pType="" format="" pidVer="2.0" timeout="" otp="" wadh="sgydIC09zzy6f8Lb3xaAqzKquKe9lFcNR9uTvYxFp+A=" posh="" /> <CustOpts><Param name="txnId" value="76435891"/><Param name="purpose" value="auth"/><Param name="language" value="en"/></CustOpts></PidOptions>"""
121
-
122
- if (pidOption.length >= 10) {
123
- finalPidOption = pidOption
119
+ override fun getFaceCapture(deviceName: String, pidOption: String, promise: Promise) {
120
+ startRdActivity(promise, deviceName, FACE_CAPTURE_CODE) {
121
+ Intent(ACTION_FACE_CAPTURE)
122
+ .putExtra("request", pidOptionOrDefault(pidOption, DEFAULT_FACE_PID_OPTIONS))
124
123
  }
124
+ }
125
125
 
126
- val intent = Intent().apply {
127
- action = "in.gov.uidai.rdservice.face.CAPTURE"
128
- putExtra("request", finalPidOption)
129
- setPackage(pckName)
126
+ private fun pidOptionOrDefault(pidOption: String, default: String): String =
127
+ if (pidOption.trim().length >= MIN_VALID_RESPONSE_LENGTH) pidOption else default
128
+
129
+ private fun startRdActivity(
130
+ promise: Promise,
131
+ packageName: String,
132
+ requestCode: Int,
133
+ intentBuilder: () -> Intent,
134
+ ) {
135
+ if (this.promise != null) {
136
+ resolvePromise(promise, FAILURE, "Another RD service request is already in progress")
137
+ return
138
+ }
139
+ if (!PACKAGE_NAME_PATTERN.matches(packageName)) {
140
+ resolvePromise(promise, FAILURE, "Invalid RD service package name")
141
+ return
142
+ }
143
+ val activity = reactApplicationContext.getCurrentActivity()
144
+ if (activity == null) {
145
+ resolvePromise(promise, FAILURE, "No foreground activity available to start RD service")
146
+ return
130
147
  }
131
148
 
132
- val currentActivity = currentActivity
149
+ this.promise = promise
133
150
  try {
134
- currentActivity?.startActivityForResult(intent, FACE_CAPTURE_CODE)
151
+ activity.startActivityForResult(intentBuilder().setPackage(packageName), requestCode)
135
152
  } catch (e: Exception) {
136
- e.printStackTrace()
153
+ // Log only the exception type; never log intent contents or biometric data.
154
+ Log.w(NAME, "Unable to start RD service activity: ${e.javaClass.simpleName}")
137
155
  resolve(FAILURE, "Selected device not found")
138
156
  }
139
157
  }
140
158
 
141
- override fun getFingerPrint(deviceName: String, pidOption: String, promise: Promise) {
142
- try {
143
- this.promise = promise
144
- this.pckName = deviceName
145
- this.pidOption = pidOption
146
- captureData()
147
- } catch (e: Exception) {
148
- e.printStackTrace()
149
- resolve(FAILURE, "RD services not available")
159
+ private fun handleInfoResult(info: String?) {
160
+ if (info == null ||
161
+ info.length <= MIN_VALID_RESPONSE_LENGTH ||
162
+ info.lowercase().contains("notready")
163
+ ) {
164
+ resolve(FAILURE, "Device not ready")
165
+ return
150
166
  }
167
+ resolve(SUCCESS, info)
151
168
  }
152
169
 
153
- override fun getFaceCapture(deviceName: String, pidOption: String, promise: Promise) {
154
- try {
155
- this.promise = promise
156
- this.pckName = deviceName
157
- this.pidOption = pidOption
158
- captureFaceData()
159
- } catch (e: Exception) {
160
- e.printStackTrace()
161
- resolve(FAILURE, "Face RD services not available")
170
+ private fun handleCaptureResult(captureXml: String?) {
171
+ if (captureXml == null || captureXml.length <= MIN_VALID_RESPONSE_LENGTH) {
172
+ resolve(FAILURE, "Device not ready")
173
+ return
162
174
  }
163
- }
164
-
165
- private fun parseBioMetricData(bioxml: String): String {
166
- return bioxml
167
- .replace("\"", "'")
168
- .replace("\n ", " ")
169
- .replace("\n ", " ")
175
+ if (captureXml.lowercase().contains("device not ready")) {
176
+ resolve(FAILURE, "Device not ready")
177
+ return
178
+ }
179
+ // Return the PID XML exactly as the RD service produced it. It is digitally
180
+ // signed; altering even a single character breaks server-side verification.
181
+ resolve(SUCCESS, captureXml)
170
182
  }
171
183
 
172
184
  private fun resolve(status: String, message: String) {
173
- if (promise == null) {
174
- return
175
- }
185
+ val pending = promise ?: return
186
+ promise = null
187
+ resolvePromise(pending, status, message)
188
+ }
176
189
 
190
+ private fun resolvePromise(promise: Promise, status: String, message: String) {
177
191
  val map: WritableMap = Arguments.createMap()
178
- map.putString("status", status.uppercase())
179
- map.putString("message", parseBioMetricData(message))
180
-
181
- promise?.resolve(map)
182
- promise = null
192
+ map.putString("status", status)
193
+ map.putString("message", message)
194
+ promise.resolve(map)
183
195
  }
184
196
  }
package/ios/Rdservice.mm CHANGED
@@ -1,16 +1,57 @@
1
1
  #import "Rdservice.h"
2
2
 
3
+ /**
4
+ * UIDAI RD (Registered Device) services are certified for Android and Windows
5
+ * only β€” the capture flow depends on RD service apps resolved via Android
6
+ * intents, which have no iOS equivalent. These implementations exist so the
7
+ * module loads on iOS and resolves with a consistent FAILURE response instead
8
+ * of crashing, letting cross-platform apps share one code path.
9
+ */
10
+ static NSDictionary *RdserviceUnsupportedResponse(void)
11
+ {
12
+ return @{
13
+ @"status" : @"FAILURE",
14
+ @"message" : @"UIDAI RD services are not available on iOS. Biometric capture is supported on Android only.",
15
+ };
16
+ }
17
+
3
18
  @implementation Rdservice
4
- - (NSNumber *)multiply:(double)a b:(double)b {
5
- NSNumber *result = @(a * b);
6
19
 
7
- return result;
20
+ - (void)getDeviceInfo:(NSString *)deviceName
21
+ resolve:(RCTPromiseResolveBlock)resolve
22
+ reject:(RCTPromiseRejectBlock)reject
23
+ {
24
+ resolve(RdserviceUnsupportedResponse());
25
+ }
26
+
27
+ - (void)getFingerPrint:(NSString *)deviceName
28
+ pidOption:(NSString *)pidOption
29
+ resolve:(RCTPromiseResolveBlock)resolve
30
+ reject:(RCTPromiseRejectBlock)reject
31
+ {
32
+ resolve(RdserviceUnsupportedResponse());
33
+ }
34
+
35
+ - (void)getIrisCapture:(NSString *)deviceName
36
+ pidOption:(NSString *)pidOption
37
+ resolve:(RCTPromiseResolveBlock)resolve
38
+ reject:(RCTPromiseRejectBlock)reject
39
+ {
40
+ resolve(RdserviceUnsupportedResponse());
41
+ }
42
+
43
+ - (void)getFaceCapture:(NSString *)deviceName
44
+ pidOption:(NSString *)pidOption
45
+ resolve:(RCTPromiseResolveBlock)resolve
46
+ reject:(RCTPromiseRejectBlock)reject
47
+ {
48
+ resolve(RdserviceUnsupportedResponse());
8
49
  }
9
50
 
10
51
  - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
11
52
  (const facebook::react::ObjCTurboModule::InitParams &)params
12
53
  {
13
- return std::make_shared<facebook::react::NativeRdserviceSpecJSI>(params);
54
+ return std::make_shared<facebook::react::NativeRdserviceSpecJSI>(params);
14
55
  }
15
56
 
16
57
  + (NSString *)moduleName
@@ -1 +1 @@
1
- {"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"..\\..\\src","sources":["NativeRdservice.ts"],"mappings":";;AAAA,SAASA,mBAAmB,QAA0B,cAAc;AAYpE,eAAeA,mBAAmB,CAACC,YAAY,CAAO,WAAW,CAAC","ignoreList":[]}
1
+ {"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"..\\..\\src","sources":["NativeRdservice.ts"],"mappings":";;AAAA,SAASA,mBAAmB,QAA0B,cAAc;AAyBpE,eAAeA,mBAAmB,CAACC,YAAY,CAAO,WAAW,CAAC","ignoreList":[]}
@@ -1,10 +1,52 @@
1
1
  "use strict";
2
2
 
3
3
  import Rdservice from "./NativeRdservice.js";
4
- export function getFingerPrint(deviceName, pidOption) {
4
+
5
+ /** Possible values of {@link RdServiceResponse.status}. */
6
+
7
+ /**
8
+ * Queries the RD service app for device/driver status (RD INFO call).
9
+ *
10
+ * @param deviceName Package name of the RD service app, e.g. `com.mantra.rdservice`.
11
+ * @returns On success, `message` contains the `RDService` info XML.
12
+ */
13
+ export function getDeviceInfo(deviceName) {
14
+ return Rdservice.getDeviceInfo(deviceName);
15
+ }
16
+
17
+ /**
18
+ * Captures fingerprint biometric data via the given RD service app.
19
+ *
20
+ * @param deviceName Package name of the RD service app, e.g. `com.mantra.rdservice`.
21
+ * @param pidOption Optional `PidOptions` XML. Falls back to a sensible
22
+ * single-finger capture configuration when omitted or blank.
23
+ * @returns On success, `message` contains the signed `PidData` XML.
24
+ */
25
+ export function getFingerPrint(deviceName, pidOption = '') {
5
26
  return Rdservice.getFingerPrint(deviceName, pidOption);
6
27
  }
7
- export function getFaceCapture(deviceName, pidOption) {
28
+
29
+ /**
30
+ * Captures iris biometric data via the given iris RD service app.
31
+ *
32
+ * @param deviceName Package name of the iris RD service app, e.g. `com.mantra.mis100v2.rdservice`.
33
+ * @param pidOption Optional `PidOptions` XML. Falls back to a single-iris
34
+ * capture configuration when omitted or blank.
35
+ * @returns On success, `message` contains the signed `PidData` XML.
36
+ */
37
+ export function getIrisCapture(deviceName, pidOption = '') {
38
+ return Rdservice.getIrisCapture(deviceName, pidOption);
39
+ }
40
+
41
+ /**
42
+ * Captures face biometric data via the UIDAI Face RD app.
43
+ *
44
+ * @param deviceName Package name of the face RD service app, e.g. `in.gov.uidai.facerd`.
45
+ * @param pidOption Optional `PidOptions` XML. The default targets face
46
+ * authentication; pass your own options (with the correct `wadh`) for eKYC.
47
+ * @returns On success, `message` contains the signed `PidData` XML.
48
+ */
49
+ export function getFaceCapture(deviceName, pidOption = '') {
8
50
  return Rdservice.getFaceCapture(deviceName, pidOption);
9
51
  }
10
52
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["Rdservice","getFingerPrint","deviceName","pidOption","getFaceCapture"],"sourceRoot":"..\\..\\src","sources":["index.tsx"],"mappings":";;AAAA,OAAOA,SAAS,MAAkC,sBAAmB;AAIrE,OAAO,SAASC,cAAcA,CAC5BC,UAAkB,EAClBC,SAAiB,EACW;EAC5B,OAAOH,SAAS,CAACC,cAAc,CAACC,UAAU,EAAEC,SAAS,CAAC;AACxD;AAEA,OAAO,SAASC,cAAcA,CAC5BF,UAAkB,EAClBC,SAAiB,EACW;EAC5B,OAAOH,SAAS,CAACI,cAAc,CAACF,UAAU,EAAEC,SAAS,CAAC;AACxD","ignoreList":[]}
1
+ {"version":3,"names":["Rdservice","getDeviceInfo","deviceName","getFingerPrint","pidOption","getIrisCapture","getFaceCapture"],"sourceRoot":"..\\..\\src","sources":["index.tsx"],"mappings":";;AAAA,OAAOA,SAAS,MAAkC,sBAAmB;;AAIrE;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,aAAaA,CAACC,UAAkB,EAA8B;EAC5E,OAAOF,SAAS,CAACC,aAAa,CAACC,UAAU,CAAC;AAC5C;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,cAAcA,CAC5BD,UAAkB,EAClBE,SAAiB,GAAG,EAAE,EACM;EAC5B,OAAOJ,SAAS,CAACG,cAAc,CAACD,UAAU,EAAEE,SAAS,CAAC;AACxD;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,cAAcA,CAC5BH,UAAkB,EAClBE,SAAiB,GAAG,EAAE,EACM;EAC5B,OAAOJ,SAAS,CAACK,cAAc,CAACH,UAAU,EAAEE,SAAS,CAAC;AACxD;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASE,cAAcA,CAC5BJ,UAAkB,EAClBE,SAAiB,GAAG,EAAE,EACM;EAC5B,OAAOJ,SAAS,CAACM,cAAc,CAACJ,UAAU,EAAEE,SAAS,CAAC;AACxD","ignoreList":[]}
@@ -1,10 +1,14 @@
1
1
  import { type TurboModule } from 'react-native';
2
2
  export interface RdServiceResponse {
3
+ /** "SUCCESS" or "FAILURE" */
3
4
  status: string;
5
+ /** Signed XML from the RD service on success, human-readable error on failure */
4
6
  message: string;
5
7
  }
6
8
  export interface Spec extends TurboModule {
9
+ getDeviceInfo(deviceName: string): Promise<RdServiceResponse>;
7
10
  getFingerPrint(deviceName: string, pidOption: string): Promise<RdServiceResponse>;
11
+ getIrisCapture(deviceName: string, pidOption: string): Promise<RdServiceResponse>;
8
12
  getFaceCapture(deviceName: string, pidOption: string): Promise<RdServiceResponse>;
9
13
  }
10
14
  declare const _default: Spec;
@@ -1 +1 @@
1
- {"version":3,"file":"NativeRdservice.d.ts","sourceRoot":"","sources":["../../../src/NativeRdservice.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAErE,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAClF,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;CACnF;;AAED,wBAAmE"}
1
+ {"version":3,"file":"NativeRdservice.d.ts","sourceRoot":"","sources":["../../../src/NativeRdservice.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAErE,MAAM,WAAW,iBAAiB;IAChC,6BAA6B;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,iFAAiF;IACjF,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAC9D,cAAc,CACZ,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAC9B,cAAc,CACZ,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAC9B,cAAc,CACZ,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,iBAAiB,CAAC,CAAC;CAC/B;;AAED,wBAAmE"}
@@ -1,5 +1,39 @@
1
- import { type RdServiceResponse } from './NativeRdservice';
1
+ import { type RdServiceResponse } from './NativeRdservice.js';
2
2
  export type { RdServiceResponse };
3
- export declare function getFingerPrint(deviceName: string, pidOption: string): Promise<RdServiceResponse>;
4
- export declare function getFaceCapture(deviceName: string, pidOption: string): Promise<RdServiceResponse>;
3
+ /** Possible values of {@link RdServiceResponse.status}. */
4
+ export type RdServiceStatus = 'SUCCESS' | 'FAILURE';
5
+ /**
6
+ * Queries the RD service app for device/driver status (RD INFO call).
7
+ *
8
+ * @param deviceName Package name of the RD service app, e.g. `com.mantra.rdservice`.
9
+ * @returns On success, `message` contains the `RDService` info XML.
10
+ */
11
+ export declare function getDeviceInfo(deviceName: string): Promise<RdServiceResponse>;
12
+ /**
13
+ * Captures fingerprint biometric data via the given RD service app.
14
+ *
15
+ * @param deviceName Package name of the RD service app, e.g. `com.mantra.rdservice`.
16
+ * @param pidOption Optional `PidOptions` XML. Falls back to a sensible
17
+ * single-finger capture configuration when omitted or blank.
18
+ * @returns On success, `message` contains the signed `PidData` XML.
19
+ */
20
+ export declare function getFingerPrint(deviceName: string, pidOption?: string): Promise<RdServiceResponse>;
21
+ /**
22
+ * Captures iris biometric data via the given iris RD service app.
23
+ *
24
+ * @param deviceName Package name of the iris RD service app, e.g. `com.mantra.mis100v2.rdservice`.
25
+ * @param pidOption Optional `PidOptions` XML. Falls back to a single-iris
26
+ * capture configuration when omitted or blank.
27
+ * @returns On success, `message` contains the signed `PidData` XML.
28
+ */
29
+ export declare function getIrisCapture(deviceName: string, pidOption?: string): Promise<RdServiceResponse>;
30
+ /**
31
+ * Captures face biometric data via the UIDAI Face RD app.
32
+ *
33
+ * @param deviceName Package name of the face RD service app, e.g. `in.gov.uidai.facerd`.
34
+ * @param pidOption Optional `PidOptions` XML. The default targets face
35
+ * authentication; pass your own options (with the correct `wadh`) for eKYC.
36
+ * @returns On success, `message` contains the signed `PidData` XML.
37
+ */
38
+ export declare function getFaceCapture(deviceName: string, pidOption?: string): Promise<RdServiceResponse>;
5
39
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA,OAAkB,EAAE,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAEtE,YAAY,EAAE,iBAAiB,EAAE,CAAC;AAElC,wBAAgB,cAAc,CAC5B,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,iBAAiB,CAAC,CAE5B;AAED,wBAAgB,cAAc,CAC5B,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,iBAAiB,CAAC,CAE5B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA,OAAkB,EAAE,KAAK,iBAAiB,EAAE,MAAM,sBAAmB,CAAC;AAEtE,YAAY,EAAE,iBAAiB,EAAE,CAAC;AAElC,2DAA2D;AAC3D,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,SAAS,CAAC;AAEpD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAE5E;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,UAAU,EAAE,MAAM,EAClB,SAAS,GAAE,MAAW,GACrB,OAAO,CAAC,iBAAiB,CAAC,CAE5B;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,UAAU,EAAE,MAAM,EAClB,SAAS,GAAE,MAAW,GACrB,OAAO,CAAC,iBAAiB,CAAC,CAE5B;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,UAAU,EAAE,MAAM,EAClB,SAAS,GAAE,MAAW,GACrB,OAAO,CAAC,iBAAiB,CAAC,CAE5B"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "react-native-rdservice",
3
- "version": "0.1.0",
4
- "description": "Reading Finger print and Face data using RD services in Android",
3
+ "version": "0.2.0",
4
+ "description": "Capture fingerprint, iris and face biometric data using UIDAI RD (Registered Device) services on Android",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
7
7
  "exports": {
@@ -45,6 +45,7 @@
45
45
  "android",
46
46
  "biometric",
47
47
  "fingerprint",
48
+ "iris",
48
49
  "face-recognition",
49
50
  "rd-service",
50
51
  "uidai",
@@ -69,33 +70,33 @@
69
70
  "registry": "https://registry.npmjs.org/"
70
71
  },
71
72
  "devDependencies": {
72
- "@commitlint/config-conventional": "^19.8.1",
73
+ "@commitlint/config-conventional": "^21.2.0",
73
74
  "@eslint/compat": "^1.3.2",
74
- "@eslint/eslintrc": "^3.3.1",
75
- "@eslint/js": "^9.35.0",
75
+ "@eslint/eslintrc": "^3.3.5",
76
+ "@eslint/js": "^9.39.4",
76
77
  "@react-native/babel-preset": "0.83.0",
77
78
  "@react-native/eslint-config": "0.83.0",
78
- "@release-it/conventional-changelog": "^10.0.1",
79
+ "@release-it/conventional-changelog": "^11.0.1",
79
80
  "@types/jest": "^29.5.14",
80
- "@types/react": "^19.2.0",
81
- "commitlint": "^19.8.1",
82
- "del-cli": "^6.0.0",
83
- "eslint": "^9.35.0",
81
+ "@types/react": "^19.2.17",
82
+ "commitlint": "^21.2.0",
83
+ "del-cli": "^7.0.0",
84
+ "eslint": "^9.39.4",
84
85
  "eslint-config-prettier": "^10.1.8",
85
- "eslint-plugin-prettier": "^5.5.4",
86
+ "eslint-plugin-prettier": "^5.5.6",
86
87
  "jest": "^29.7.0",
87
- "lefthook": "^2.0.3",
88
- "prettier": "^2.8.8",
88
+ "lefthook": "^2.1.9",
89
+ "prettier": "^3.9.4",
89
90
  "react": "19.2.0",
90
91
  "react-native": "0.83.0",
91
- "react-native-builder-bob": "^0.40.13",
92
- "release-it": "^19.0.4",
93
- "turbo": "^2.5.6",
94
- "typescript": "^5.9.2"
92
+ "react-native-builder-bob": "^0.43.0",
93
+ "release-it": "^20.2.1",
94
+ "turbo": "^2.10.4",
95
+ "typescript": "^5.9.3"
95
96
  },
96
97
  "peerDependencies": {
97
98
  "react": "*",
98
- "react-native": "*"
99
+ "react-native": ">=0.80.0"
99
100
  },
100
101
  "workspaces": [
101
102
  "example"
@@ -1,13 +1,26 @@
1
1
  import { TurboModuleRegistry, type TurboModule } from 'react-native';
2
2
 
3
3
  export interface RdServiceResponse {
4
+ /** "SUCCESS" or "FAILURE" */
4
5
  status: string;
6
+ /** Signed XML from the RD service on success, human-readable error on failure */
5
7
  message: string;
6
8
  }
7
9
 
8
10
  export interface Spec extends TurboModule {
9
- getFingerPrint(deviceName: string, pidOption: string): Promise<RdServiceResponse>;
10
- getFaceCapture(deviceName: string, pidOption: string): Promise<RdServiceResponse>;
11
+ getDeviceInfo(deviceName: string): Promise<RdServiceResponse>;
12
+ getFingerPrint(
13
+ deviceName: string,
14
+ pidOption: string
15
+ ): Promise<RdServiceResponse>;
16
+ getIrisCapture(
17
+ deviceName: string,
18
+ pidOption: string
19
+ ): Promise<RdServiceResponse>;
20
+ getFaceCapture(
21
+ deviceName: string,
22
+ pidOption: string
23
+ ): Promise<RdServiceResponse>;
11
24
  }
12
25
 
13
26
  export default TurboModuleRegistry.getEnforcing<Spec>('Rdservice');
package/src/index.tsx CHANGED
@@ -2,16 +2,60 @@ import Rdservice, { type RdServiceResponse } from './NativeRdservice';
2
2
 
3
3
  export type { RdServiceResponse };
4
4
 
5
+ /** Possible values of {@link RdServiceResponse.status}. */
6
+ export type RdServiceStatus = 'SUCCESS' | 'FAILURE';
7
+
8
+ /**
9
+ * Queries the RD service app for device/driver status (RD INFO call).
10
+ *
11
+ * @param deviceName Package name of the RD service app, e.g. `com.mantra.rdservice`.
12
+ * @returns On success, `message` contains the `RDService` info XML.
13
+ */
14
+ export function getDeviceInfo(deviceName: string): Promise<RdServiceResponse> {
15
+ return Rdservice.getDeviceInfo(deviceName);
16
+ }
17
+
18
+ /**
19
+ * Captures fingerprint biometric data via the given RD service app.
20
+ *
21
+ * @param deviceName Package name of the RD service app, e.g. `com.mantra.rdservice`.
22
+ * @param pidOption Optional `PidOptions` XML. Falls back to a sensible
23
+ * single-finger capture configuration when omitted or blank.
24
+ * @returns On success, `message` contains the signed `PidData` XML.
25
+ */
5
26
  export function getFingerPrint(
6
27
  deviceName: string,
7
- pidOption: string
28
+ pidOption: string = ''
8
29
  ): Promise<RdServiceResponse> {
9
30
  return Rdservice.getFingerPrint(deviceName, pidOption);
10
31
  }
11
32
 
33
+ /**
34
+ * Captures iris biometric data via the given iris RD service app.
35
+ *
36
+ * @param deviceName Package name of the iris RD service app, e.g. `com.mantra.mis100v2.rdservice`.
37
+ * @param pidOption Optional `PidOptions` XML. Falls back to a single-iris
38
+ * capture configuration when omitted or blank.
39
+ * @returns On success, `message` contains the signed `PidData` XML.
40
+ */
41
+ export function getIrisCapture(
42
+ deviceName: string,
43
+ pidOption: string = ''
44
+ ): Promise<RdServiceResponse> {
45
+ return Rdservice.getIrisCapture(deviceName, pidOption);
46
+ }
47
+
48
+ /**
49
+ * Captures face biometric data via the UIDAI Face RD app.
50
+ *
51
+ * @param deviceName Package name of the face RD service app, e.g. `in.gov.uidai.facerd`.
52
+ * @param pidOption Optional `PidOptions` XML. The default targets face
53
+ * authentication; pass your own options (with the correct `wadh`) for eKYC.
54
+ * @returns On success, `message` contains the signed `PidData` XML.
55
+ */
12
56
  export function getFaceCapture(
13
57
  deviceName: string,
14
- pidOption: string
58
+ pidOption: string = ''
15
59
  ): Promise<RdServiceResponse> {
16
60
  return Rdservice.getFaceCapture(deviceName, pidOption);
17
61
  }