react-native-rdservice 0.1.1 β 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 +202 -221
- package/android/build.gradle +3 -1
- package/android/src/main/AndroidManifest.xml +19 -0
- package/android/src/main/java/com/rdservice/RdserviceModule.kt +129 -117
- package/ios/Rdservice.mm +45 -4
- package/lib/module/NativeRdservice.js.map +1 -1
- package/lib/module/index.js +44 -2
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/NativeRdservice.d.ts +4 -0
- package/lib/typescript/src/NativeRdservice.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +37 -3
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +20 -19
- package/src/NativeRdservice.ts +7 -0
- package/src/index.tsx +46 -2
package/README.md
CHANGED
|
@@ -1,58 +1,112 @@
|
|
|
1
1
|
# react-native-rdservice
|
|
2
2
|
|
|
3
|
-
A React Native library for capturing
|
|
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
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/react-native-rdservice)
|
|
6
|
+
[](LICENSE)
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
44
|
+
```typescript
|
|
45
|
+
import { getFingerPrint } from 'react-native-rdservice';
|
|
46
|
+
|
|
47
|
+
const result = await getFingerPrint('com.mantra.rdservice');
|
|
26
48
|
|
|
27
|
-
|
|
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
|
-
|
|
56
|
+
## API Reference
|
|
30
57
|
|
|
31
|
-
|
|
58
|
+
All methods return `Promise<RdServiceResponse>`:
|
|
32
59
|
|
|
33
60
|
```typescript
|
|
34
61
|
interface RdServiceResponse {
|
|
35
|
-
status: string;
|
|
36
|
-
message: string;
|
|
62
|
+
status: string; // 'SUCCESS' | 'FAILURE'
|
|
63
|
+
message: string; // signed XML on success, human-readable error on failure
|
|
37
64
|
}
|
|
38
65
|
```
|
|
39
66
|
|
|
40
|
-
|
|
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
|
-
|
|
86
|
+
### `getFingerPrint(deviceName, pidOption?)`
|
|
45
87
|
|
|
46
|
-
Captures fingerprint biometric data
|
|
88
|
+
Captures fingerprint biometric data.
|
|
47
89
|
|
|
48
|
-
|
|
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
|
-
|
|
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
|
-
|
|
119
|
+
---
|
|
66
120
|
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
79
|
-
|
|
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
|
-
|
|
84
|
-
|
|
85
|
-
```typescript
|
|
86
|
-
import { getFingerPrint } from 'react-native-rdservice';
|
|
136
|
+
Default `PidOptions`:
|
|
87
137
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
-
|
|
149
|
+
### `getFaceCapture(deviceName, pidOption?)`
|
|
150
|
+
|
|
151
|
+
Captures face biometric data via the UIDAI Face RD app.
|
|
128
152
|
|
|
129
|
-
|
|
153
|
+
```typescript
|
|
154
|
+
import { getFaceCapture } from 'react-native-rdservice';
|
|
130
155
|
|
|
131
|
-
|
|
156
|
+
const result = await getFaceCapture('in.gov.uidai.facerd');
|
|
157
|
+
```
|
|
132
158
|
|
|
133
|
-
|
|
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
|
|
177
|
+
## Complete usage example
|
|
219
178
|
|
|
220
179
|
```typescript
|
|
221
180
|
import React, { useState } from 'react';
|
|
222
|
-
import { View, Button,
|
|
223
|
-
import {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
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
|
|
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
|
-
|
|
257
|
-
Alert.alert('Success', '
|
|
198
|
+
// Forward result.message (signed PID XML) to your backend over TLS.
|
|
199
|
+
Alert.alert('Success', 'Biometric captured');
|
|
258
200
|
} else {
|
|
259
|
-
Alert.alert('
|
|
201
|
+
Alert.alert('Failed', result.message);
|
|
260
202
|
}
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
console.error(error);
|
|
203
|
+
} finally {
|
|
204
|
+
setBusy(false);
|
|
264
205
|
}
|
|
265
206
|
};
|
|
266
207
|
|
|
267
208
|
return (
|
|
268
209
|
<View>
|
|
269
|
-
<Button
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
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
|
|
235
|
+
## Common RD service package names
|
|
282
236
|
|
|
283
|
-
**Fingerprint
|
|
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
|
-
|
|
291
|
-
|
|
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
|
-
|
|
248
|
+
**Iris:**
|
|
294
249
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
250
|
+
| Vendor | Package |
|
|
251
|
+
|--------|---------|
|
|
252
|
+
| Mantra MIS100V2 | `com.mantra.mis100v2.rdservice` |
|
|
253
|
+
| IriTech IriShield | `com.iritech.rdservice` |
|
|
298
254
|
|
|
299
|
-
|
|
255
|
+
**Face:**
|
|
300
256
|
|
|
301
|
-
|
|
302
|
-
|
|
257
|
+
| Vendor | Package |
|
|
258
|
+
|--------|---------|
|
|
259
|
+
| UIDAI Face RD | `in.gov.uidai.facerd` |
|
|
303
260
|
|
|
304
|
-
|
|
261
|
+
> Package names can change between vendor releases β verify against the RD service app actually installed on your target devices.
|
|
305
262
|
|
|
306
|
-
|
|
263
|
+
## Error handling
|
|
307
264
|
|
|
308
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
package/android/build.gradle
CHANGED
|
@@ -74,6 +74,8 @@ repositories {
|
|
|
74
74
|
def kotlin_version = getExtOrDefault("kotlinVersion")
|
|
75
75
|
|
|
76
76
|
dependencies {
|
|
77
|
-
|
|
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,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
|
-
|
|
20
|
-
const val
|
|
21
|
-
const val
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
98
|
-
|
|
93
|
+
override fun invalidate() {
|
|
94
|
+
reactApplicationContext.removeActivityEventListener(activityEventListener)
|
|
95
|
+
promise = null
|
|
96
|
+
super.invalidate()
|
|
97
|
+
}
|
|
99
98
|
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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
|
-
|
|
149
|
+
this.promise = promise
|
|
133
150
|
try {
|
|
134
|
-
|
|
151
|
+
activity.startActivityForResult(intentBuilder().setPackage(packageName), requestCode)
|
|
135
152
|
} catch (e: Exception) {
|
|
136
|
-
|
|
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
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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
|
-
|
|
174
|
-
|
|
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
|
|
179
|
-
map.putString("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
|
-
|
|
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
|
-
|
|
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;
|
|
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":[]}
|
package/lib/module/index.js
CHANGED
|
@@ -1,10 +1,52 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
import Rdservice from "./NativeRdservice.js";
|
|
4
|
-
|
|
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
|
-
|
|
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
|
package/lib/module/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["Rdservice","
|
|
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,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
|
+
{"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
|
-
|
|
4
|
-
export
|
|
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,
|
|
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.
|
|
4
|
-
"description": "
|
|
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": "^
|
|
73
|
+
"@commitlint/config-conventional": "^21.2.0",
|
|
73
74
|
"@eslint/compat": "^1.3.2",
|
|
74
|
-
"@eslint/eslintrc": "^3.3.
|
|
75
|
-
"@eslint/js": "^9.
|
|
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": "^
|
|
79
|
+
"@release-it/conventional-changelog": "^11.0.1",
|
|
79
80
|
"@types/jest": "^29.5.14",
|
|
80
|
-
"@types/react": "^19.2.
|
|
81
|
-
"commitlint": "^
|
|
82
|
-
"del-cli": "^
|
|
83
|
-
"eslint": "^9.
|
|
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.
|
|
86
|
+
"eslint-plugin-prettier": "^5.5.6",
|
|
86
87
|
"jest": "^29.7.0",
|
|
87
|
-
"lefthook": "^2.
|
|
88
|
-
"prettier": "^
|
|
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.
|
|
92
|
-
"release-it": "^
|
|
93
|
-
"turbo": "^2.
|
|
94
|
-
"typescript": "^5.9.
|
|
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"
|
|
@@ -176,4 +177,4 @@
|
|
|
176
177
|
],
|
|
177
178
|
"version": "0.56.0"
|
|
178
179
|
}
|
|
179
|
-
}
|
|
180
|
+
}
|
package/src/NativeRdservice.ts
CHANGED
|
@@ -1,15 +1,22 @@
|
|
|
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 {
|
|
11
|
+
getDeviceInfo(deviceName: string): Promise<RdServiceResponse>;
|
|
9
12
|
getFingerPrint(
|
|
10
13
|
deviceName: string,
|
|
11
14
|
pidOption: string
|
|
12
15
|
): Promise<RdServiceResponse>;
|
|
16
|
+
getIrisCapture(
|
|
17
|
+
deviceName: string,
|
|
18
|
+
pidOption: string
|
|
19
|
+
): Promise<RdServiceResponse>;
|
|
13
20
|
getFaceCapture(
|
|
14
21
|
deviceName: string,
|
|
15
22
|
pidOption: string
|
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
|
}
|