@passgage/sdk-react-native 1.0.3 → 1.0.5
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 +464 -70
- package/dist/index.d.mts +134 -424
- package/dist/index.d.ts +134 -424
- package/dist/index.js +930 -1197
- package/dist/index.mjs +868 -1132
- package/package.json +8 -4
package/README.md
CHANGED
|
@@ -1,27 +1,38 @@
|
|
|
1
|
-
# @passgage-
|
|
1
|
+
# @passgage/sdk-react-native
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Complete Passgage Access SDK for React Native with authentication, QR/NFC scanning, check-in, and remote work features.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npm install @passgage-
|
|
8
|
+
npm install @passgage/sdk-react-native
|
|
9
9
|
# or
|
|
10
|
-
yarn add @passgage-
|
|
10
|
+
yarn add @passgage/sdk-react-native
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
+
## Dependencies
|
|
14
|
+
|
|
15
|
+
The package has the following dependencies:
|
|
16
|
+
|
|
17
|
+
- **axios**: ^1.6.0 - HTTP client for API requests
|
|
18
|
+
- **react-native-geolocation-service**: ^5.3.1 - Geolocation service
|
|
19
|
+
- **react-native-permissions**: ^5.4.4 - Permissions management
|
|
20
|
+
- **zustand**: ^5.0.9 - State management
|
|
21
|
+
|
|
13
22
|
### Peer Dependencies
|
|
14
23
|
|
|
15
24
|
You also need to install these peer dependencies:
|
|
16
25
|
|
|
17
26
|
```bash
|
|
18
|
-
npm install react-native-vision-camera react-native-nfc-manager @react-native-community/geolocation
|
|
27
|
+
npm install react-native-vision-camera react-native-nfc-manager @react-native-community/geolocation react-native-keychain
|
|
19
28
|
```
|
|
20
29
|
|
|
21
30
|
Follow the installation instructions for each native library:
|
|
31
|
+
|
|
22
32
|
- [react-native-vision-camera](https://react-native-vision-camera.com/docs/guides)
|
|
23
33
|
- [react-native-nfc-manager](https://github.com/revtel/react-native-nfc-manager)
|
|
24
|
-
- [
|
|
34
|
+
- [react-native-geolocation-service](https://github.com/Agontuk/react-native-geolocation-service)
|
|
35
|
+
- [react-native-keychain](https://github.com/oblador/react-native-keychain)
|
|
25
36
|
|
|
26
37
|
## Usage
|
|
27
38
|
|
|
@@ -30,127 +41,510 @@ Follow the installation instructions for each native library:
|
|
|
30
41
|
Wrap your app with `PassgageAccessProvider`:
|
|
31
42
|
|
|
32
43
|
```typescript
|
|
33
|
-
import {
|
|
44
|
+
import {PassgageAccessProvider} from '@passgage/sdk-react-native';
|
|
34
45
|
|
|
35
46
|
function App() {
|
|
36
47
|
return (
|
|
37
48
|
<PassgageAccessProvider
|
|
38
49
|
baseURL="https://your-api.passgage.com"
|
|
39
|
-
|
|
40
|
-
|
|
50
|
+
msalToken={'Your MSAL Token'}
|
|
51
|
+
apiVersion="v2" // Optional: API version (default: "v2")
|
|
52
|
+
timeout={30000} // Optional: Request timeout in ms (default: 30000)
|
|
53
|
+
rememberUser={true} // Optional: Persist authentication (default: true)
|
|
54
|
+
onUnauthorized={error => {
|
|
55
|
+
// Handle session expiration
|
|
56
|
+
console.log('Unauthorized error:', error);
|
|
57
|
+
}}
|
|
58
|
+
getLocationErrorCallback={error => {
|
|
59
|
+
// Handle location retrieval errors
|
|
60
|
+
console.log('Location error:', error);
|
|
61
|
+
}}
|
|
62
|
+
locationPermissionErrorCallback={error => {
|
|
63
|
+
// Handle location permission errors
|
|
64
|
+
console.log('Permission error:', error);
|
|
65
|
+
}}>
|
|
41
66
|
<YourApp />
|
|
42
67
|
</PassgageAccessProvider>
|
|
43
68
|
);
|
|
44
69
|
}
|
|
45
70
|
```
|
|
46
71
|
|
|
47
|
-
|
|
72
|
+
#### Provider Props
|
|
73
|
+
|
|
74
|
+
| Prop | Type | Required | Default | Description |
|
|
75
|
+
|------|------|----------|---------|-------------|
|
|
76
|
+
| `baseURL` | `string` | ✓ | - | Base URL of your Passgage API |
|
|
77
|
+
| `msalToken` | `string` | ✓ | - | Microsoft Azure AD token |
|
|
78
|
+
| `apiVersion` | `string` | ✗ | `"v2"` | API version to use |
|
|
79
|
+
| `timeout` | `number` | ✗ | `30000` | Request timeout in milliseconds |
|
|
80
|
+
| `rememberUser` | `boolean` | ✗ | `true` | Persist authentication state |
|
|
81
|
+
| `onUnauthorized` | `(error: Error) => void` | ✗ | - | Called when session expires |
|
|
82
|
+
| `getLocationErrorCallback` | `(error: any) => void` | ✗ | - | Called on location retrieval errors |
|
|
83
|
+
| `locationPermissionErrorCallback` | `(error: any) => void` | ✗ | - | Called on location permission errors |
|
|
84
|
+
|
|
85
|
+
### Authentication
|
|
48
86
|
|
|
49
87
|
```typescript
|
|
50
|
-
|
|
88
|
+
const LoginScreen = () => {
|
|
89
|
+
const {loading, error, loginWithAzure} = useAuthStore();
|
|
90
|
+
|
|
91
|
+
const handleLogin = async idToken => {
|
|
92
|
+
if (result?.idToken) {
|
|
93
|
+
loginWithAzure({id_token: result.idToken});
|
|
94
|
+
} else {
|
|
95
|
+
Alert.alert('Login Failed', 'No ID token received');
|
|
96
|
+
}
|
|
97
|
+
};
|
|
51
98
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
99
|
+
return (
|
|
100
|
+
<View style={styles.formContainer}>
|
|
101
|
+
{error && (
|
|
102
|
+
<View style={styles.errorContainer}>
|
|
103
|
+
<Text style={styles.errorText}>{error}</Text>
|
|
104
|
+
</View>
|
|
105
|
+
)}
|
|
106
|
+
<TouchableOpacity
|
|
107
|
+
style={[styles.button, loading && styles.buttonDisabled]}
|
|
108
|
+
onPress={() => handleLogin('Provide your msal token')}
|
|
109
|
+
disabled={loading}>
|
|
110
|
+
{loading ? (
|
|
111
|
+
<ActivityIndicator color="#fff" />
|
|
112
|
+
) : (
|
|
113
|
+
<Text style={styles.buttonText}>Sign In</Text>
|
|
114
|
+
)}
|
|
115
|
+
</TouchableOpacity>
|
|
116
|
+
</View>
|
|
117
|
+
);
|
|
118
|
+
};
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### QR Scanner
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
const QRScannerScreen = () => {
|
|
125
|
+
const [qrCode, setQrCode] = useState('c70fc3a2-fbfb-4ca6-adcb-71ac0b796836');
|
|
126
|
+
|
|
127
|
+
const {scan, isLoading} = usePassgageQRScanner({
|
|
128
|
+
options: {skipLocationCheck: false, skipRepetitiveCheck: false},
|
|
129
|
+
onSuccess: entrance => {
|
|
130
|
+
const message = `Access granted!\nEntrance ID: ${entrance?.id || 'N/A'}`;
|
|
131
|
+
Alert.alert('Success', message);
|
|
56
132
|
},
|
|
57
|
-
onError:
|
|
58
|
-
|
|
133
|
+
onError: error => {
|
|
134
|
+
const errorMessage = error.message || 'QR validation failed';
|
|
135
|
+
Alert.alert('Failed', errorMessage);
|
|
59
136
|
},
|
|
60
137
|
});
|
|
61
138
|
|
|
139
|
+
const handleScan = async () => {
|
|
140
|
+
if (!qrCode.trim()) {
|
|
141
|
+
Alert.alert('Error', 'Please enter a QR code');
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
await scan(qrCode.trim());
|
|
147
|
+
} catch (error: any) {
|
|
148
|
+
// Error already handled by onError callback
|
|
149
|
+
console.error('QR scan error:', error);
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const handleClear = () => {
|
|
154
|
+
setQrCode('');
|
|
155
|
+
};
|
|
156
|
+
|
|
62
157
|
return (
|
|
63
|
-
<View>
|
|
64
|
-
{
|
|
65
|
-
|
|
158
|
+
<View style={styles.container}>
|
|
159
|
+
<View style={styles.content}>
|
|
160
|
+
<Text style={styles.icon}>📱</Text>
|
|
161
|
+
<Text style={styles.title}>QR Code Scanner</Text>
|
|
162
|
+
<Text style={styles.description}>
|
|
163
|
+
Enter a QR code to validate access
|
|
164
|
+
</Text>
|
|
165
|
+
|
|
166
|
+
<View style={styles.inputContainer}>
|
|
167
|
+
<TextInput
|
|
168
|
+
style={styles.input}
|
|
169
|
+
placeholder="Enter QR code"
|
|
170
|
+
value={qrCode}
|
|
171
|
+
onChangeText={setQrCode}
|
|
172
|
+
autoCapitalize="none"
|
|
173
|
+
editable={!isLoading}
|
|
174
|
+
returnKeyType="done"
|
|
175
|
+
onSubmitEditing={handleScan}
|
|
176
|
+
/>
|
|
177
|
+
</View>
|
|
178
|
+
|
|
179
|
+
<View style={styles.buttonContainer}>
|
|
180
|
+
<TouchableOpacity
|
|
181
|
+
style={[
|
|
182
|
+
styles.button,
|
|
183
|
+
styles.scanButton,
|
|
184
|
+
isLoading && styles.buttonDisabled,
|
|
185
|
+
]}
|
|
186
|
+
onPress={handleScan}
|
|
187
|
+
disabled={isLoading || !qrCode.trim()}>
|
|
188
|
+
{isLoading ? (
|
|
189
|
+
<ActivityIndicator color="#fff" />
|
|
190
|
+
) : (
|
|
191
|
+
<Text style={styles.buttonText}>Validate QR Code</Text>
|
|
192
|
+
)}
|
|
193
|
+
</TouchableOpacity>
|
|
194
|
+
{qrCode.trim() && !isLoading && (
|
|
195
|
+
<TouchableOpacity
|
|
196
|
+
style={[styles.button, styles.clearButton]}
|
|
197
|
+
onPress={handleClear}>
|
|
198
|
+
<Text style={styles.clearButtonText}>Clear</Text>
|
|
199
|
+
</TouchableOpacity>
|
|
200
|
+
)}
|
|
201
|
+
</View>
|
|
202
|
+
<View style={styles.info}>
|
|
203
|
+
<Text style={styles.infoText}>
|
|
204
|
+
💡 In a real app, you would use react-native-vision-camera to scan
|
|
205
|
+
QR codes with the camera
|
|
206
|
+
</Text>
|
|
207
|
+
<Text style={styles.infoText}>
|
|
208
|
+
{'\n'}For testing, you can enter any QR code value manually
|
|
209
|
+
</Text>
|
|
210
|
+
</View>
|
|
211
|
+
</View>
|
|
212
|
+
</View>
|
|
213
|
+
);
|
|
214
|
+
};
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### NFC Scanner
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
export default function NFCScannerScreen() {
|
|
221
|
+
NfcManager.start();
|
|
222
|
+
|
|
223
|
+
const {nfcData, supportNFC, startScanning, stopScanning, isScanning, error} =
|
|
224
|
+
usePassgageNFCScanner({
|
|
225
|
+
options: {skipLocationCheck: false, skipRepetitiveCheck: false},
|
|
226
|
+
onSuccess: entrance => {
|
|
227
|
+
const message = `Access granted!\nEntrance ID: ${
|
|
228
|
+
entrance?.id || 'N/A'
|
|
229
|
+
}`;
|
|
230
|
+
Alert.alert('Success', message);
|
|
231
|
+
},
|
|
232
|
+
onError: error => {
|
|
233
|
+
const errorMessage = error.message || 'NFC validation failed';
|
|
234
|
+
Alert.alert('Failed', errorMessage);
|
|
235
|
+
},
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
const handleStartScan = async () => {
|
|
239
|
+
startScanning();
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
const handleStopScan = async () => {
|
|
243
|
+
try {
|
|
244
|
+
await stopScanning();
|
|
245
|
+
} catch (error: any) {
|
|
246
|
+
console.error('Stop NFC scan error:', error);
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
return (
|
|
250
|
+
<View style={styles.container}>
|
|
251
|
+
<View style={styles.content}>
|
|
252
|
+
<Text style={styles.icon}>💳</Text>
|
|
253
|
+
<Text style={styles.title}>NFC Card Scanner</Text>
|
|
254
|
+
<Text style={styles.description}>
|
|
255
|
+
{isScanning
|
|
256
|
+
? 'Hold your device near an NFC card'
|
|
257
|
+
: 'Tap the button below to start scanning'}
|
|
258
|
+
</Text>
|
|
259
|
+
|
|
260
|
+
<View style={styles.scanStatus}>
|
|
261
|
+
{isScanning ? (
|
|
262
|
+
<>
|
|
263
|
+
<ActivityIndicator size="large" color="#FF9500" />
|
|
264
|
+
<Text style={styles.statusText}>Scanning for NFC cards...</Text>
|
|
265
|
+
</>
|
|
266
|
+
) : (
|
|
267
|
+
<View style={styles.readyIndicator}>
|
|
268
|
+
<Text style={styles.readyText}>
|
|
269
|
+
{supportNFC
|
|
270
|
+
? 'Ready to scan'
|
|
271
|
+
: supportNFC === false
|
|
272
|
+
? 'Device not support NFC'
|
|
273
|
+
: 'LOADING...'}
|
|
274
|
+
</Text>
|
|
275
|
+
</View>
|
|
276
|
+
)}
|
|
277
|
+
</View>
|
|
278
|
+
|
|
279
|
+
<View style={styles.buttonContainer}>
|
|
280
|
+
{!isScanning ? (
|
|
281
|
+
<TouchableOpacity
|
|
282
|
+
style={[styles.button, styles.scanButton]}
|
|
283
|
+
onPress={async () => {
|
|
284
|
+
handleStartScan();
|
|
285
|
+
}}>
|
|
286
|
+
<Text style={styles.buttonText}>Start NFC Scan</Text>
|
|
287
|
+
</TouchableOpacity>
|
|
288
|
+
) : (
|
|
289
|
+
<TouchableOpacity
|
|
290
|
+
style={[styles.button, styles.stopButton]}
|
|
291
|
+
onPress={handleStopScan}>
|
|
292
|
+
<Text style={styles.buttonText}>Stop Scanning</Text>
|
|
293
|
+
</TouchableOpacity>
|
|
294
|
+
)}
|
|
295
|
+
</View>
|
|
296
|
+
|
|
297
|
+
{error && (
|
|
298
|
+
<View style={styles.errorContainer}>
|
|
299
|
+
<Text style={styles.errorText}>{error.message}</Text>
|
|
300
|
+
</View>
|
|
301
|
+
)}
|
|
302
|
+
|
|
303
|
+
<View style={styles.info}>
|
|
304
|
+
<Text style={styles.infoTitle}>How it works:</Text>
|
|
305
|
+
<Text style={styles.infoText}>
|
|
306
|
+
1. Tap "Start NFC Scan" button{'\n'}
|
|
307
|
+
2. Hold your device near an NFC card{'\n'}
|
|
308
|
+
3. Wait for validation response{'\n'}
|
|
309
|
+
4. Access will be granted if authorized
|
|
310
|
+
</Text>
|
|
311
|
+
<Text style={styles.infoNote}>
|
|
312
|
+
{'\n'}💡 Make sure NFC is enabled in your device settings
|
|
313
|
+
</Text>
|
|
314
|
+
</View>
|
|
315
|
+
</View>
|
|
66
316
|
</View>
|
|
67
317
|
);
|
|
68
318
|
}
|
|
69
319
|
```
|
|
70
320
|
|
|
71
|
-
|
|
321
|
+
## API
|
|
322
|
+
|
|
323
|
+
### Hooks
|
|
324
|
+
|
|
325
|
+
All hooks use the `usePassgage*` prefix to avoid naming conflicts:
|
|
326
|
+
|
|
327
|
+
#### `usePassgageQRScanner(options)`
|
|
328
|
+
|
|
329
|
+
QR code scanning with validation and location checking.
|
|
72
330
|
|
|
73
331
|
```typescript
|
|
74
|
-
|
|
332
|
+
const {scan, isLoading} = usePassgageQRScanner({
|
|
333
|
+
options: {
|
|
334
|
+
skipLocationCheck: false, // Skip location verification
|
|
335
|
+
skipRepetitiveCheck: false, // Skip duplicate scan detection
|
|
336
|
+
},
|
|
337
|
+
onSuccess: (entrance) => {
|
|
338
|
+
console.log('Access granted:', entrance);
|
|
339
|
+
},
|
|
340
|
+
onError: (error) => {
|
|
341
|
+
console.error('Access denied:', error);
|
|
342
|
+
},
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
// Scan a QR code
|
|
346
|
+
await scan(qrCode);
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
#### `usePassgageNFCScanner(options)`
|
|
350
|
+
|
|
351
|
+
NFC card scanning with validation and location checking.
|
|
75
352
|
|
|
76
|
-
|
|
77
|
-
|
|
353
|
+
```typescript
|
|
354
|
+
const {nfcData, supportNFC, startScanning, stopScanning, isScanning, error} =
|
|
355
|
+
usePassgageNFCScanner({
|
|
356
|
+
options: {
|
|
357
|
+
skipLocationCheck: false,
|
|
358
|
+
skipRepetitiveCheck: false,
|
|
359
|
+
},
|
|
78
360
|
onSuccess: (entrance) => {
|
|
79
|
-
console.log('
|
|
361
|
+
console.log('Access granted:', entrance);
|
|
80
362
|
},
|
|
81
363
|
onError: (error) => {
|
|
82
|
-
console.error('
|
|
364
|
+
console.error('Access denied:', error);
|
|
83
365
|
},
|
|
84
366
|
});
|
|
85
367
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
return () => stopScanning();
|
|
89
|
-
}, []);
|
|
368
|
+
// Start NFC scanning
|
|
369
|
+
await startScanning();
|
|
90
370
|
|
|
91
|
-
|
|
92
|
-
|
|
371
|
+
// Stop NFC scanning
|
|
372
|
+
await stopScanning();
|
|
93
373
|
```
|
|
94
374
|
|
|
95
|
-
|
|
375
|
+
**Returns:**
|
|
376
|
+
- `nfcData?: string` - Last scanned NFC card data
|
|
377
|
+
- `supportNFC?: boolean` - Whether device supports NFC
|
|
378
|
+
- `startScanning: () => Promise<void>` - Start NFC scanning
|
|
379
|
+
- `stopScanning: () => Promise<void>` - Stop NFC scanning
|
|
380
|
+
- `isScanning: boolean` - Whether currently scanning
|
|
381
|
+
- `error: Error | null` - Last error if any
|
|
382
|
+
|
|
383
|
+
#### `useLocation()`
|
|
384
|
+
|
|
385
|
+
Access current user location and location utilities.
|
|
96
386
|
|
|
97
387
|
```typescript
|
|
98
|
-
|
|
388
|
+
const {location, error, refreshLocation} = useLocation();
|
|
99
389
|
|
|
100
|
-
|
|
101
|
-
|
|
390
|
+
// Get current location
|
|
391
|
+
console.log(location?.latitude, location?.longitude);
|
|
102
392
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
entranceType: 0, // 0 = ENTRY, 1 = EXIT
|
|
107
|
-
});
|
|
108
|
-
};
|
|
393
|
+
// Manually refresh location
|
|
394
|
+
await refreshLocation();
|
|
395
|
+
```
|
|
109
396
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
397
|
+
**Returns:**
|
|
398
|
+
- `location?: Location` - Current location coordinates
|
|
399
|
+
- `error?: string` - Location error if any
|
|
400
|
+
- `refreshLocation: () => Promise<void>` - Manually refresh location
|
|
401
|
+
|
|
402
|
+
### State Management
|
|
403
|
+
|
|
404
|
+
#### `useAuthStore()`
|
|
405
|
+
|
|
406
|
+
Zustand store for authentication state management.
|
|
407
|
+
|
|
408
|
+
```typescript
|
|
409
|
+
import {useAuthStore} from '@passgage/sdk-react-native';
|
|
410
|
+
|
|
411
|
+
const {
|
|
412
|
+
user,
|
|
413
|
+
loading,
|
|
414
|
+
error,
|
|
415
|
+
authStatus,
|
|
416
|
+
login,
|
|
417
|
+
loginWithAzure,
|
|
418
|
+
logout,
|
|
419
|
+
restoreAuth,
|
|
420
|
+
refreshToken,
|
|
421
|
+
clearError,
|
|
422
|
+
} = useAuthStore();
|
|
423
|
+
|
|
424
|
+
// Login with Azure AD
|
|
425
|
+
await loginWithAzure({id_token: 'your-azure-token'});
|
|
426
|
+
|
|
427
|
+
// Check auth status
|
|
428
|
+
if (authStatus === 'authenticated') {
|
|
429
|
+
console.log('User:', user);
|
|
119
430
|
}
|
|
431
|
+
|
|
432
|
+
// Logout
|
|
433
|
+
await logout();
|
|
434
|
+
|
|
435
|
+
// Restore previous session
|
|
436
|
+
await restoreAuth();
|
|
437
|
+
|
|
438
|
+
// Manually refresh token
|
|
439
|
+
await refreshToken();
|
|
440
|
+
|
|
441
|
+
// Clear error state
|
|
442
|
+
clearError();
|
|
120
443
|
```
|
|
121
444
|
|
|
122
|
-
|
|
445
|
+
**State:**
|
|
446
|
+
- `user?: User` - Current authenticated user
|
|
447
|
+
- `loading: boolean` - Loading state
|
|
448
|
+
- `error: string | null` - Error message if any
|
|
449
|
+
- `authStatus: 'idle' | 'loading' | 'authenticated' | 'unauthenticated'` - Authentication status
|
|
450
|
+
|
|
451
|
+
**Methods:**
|
|
452
|
+
- `login(credentials)` - Login with email/password
|
|
453
|
+
- `loginWithAzure(params)` - Login with Azure AD token
|
|
454
|
+
- `logout()` - Logout and clear session
|
|
455
|
+
- `restoreAuth()` - Restore session from secure storage
|
|
456
|
+
- `refreshToken()` - Manually refresh access token
|
|
457
|
+
- `clearError()` - Clear error state
|
|
458
|
+
|
|
459
|
+
### Services
|
|
460
|
+
|
|
461
|
+
Low-level services for advanced use cases. Most developers should use hooks instead.
|
|
462
|
+
|
|
463
|
+
#### `AuthService`
|
|
123
464
|
|
|
124
465
|
```typescript
|
|
125
|
-
import {
|
|
466
|
+
import {AuthService} from '@passgage/sdk-react-native';
|
|
126
467
|
|
|
127
|
-
|
|
128
|
-
|
|
468
|
+
const authService = new AuthService(apiClient);
|
|
469
|
+
await authService.loginWithAzure({id_token: 'token'});
|
|
470
|
+
await authService.logout();
|
|
471
|
+
```
|
|
129
472
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
473
|
+
#### `QRAccessService`
|
|
474
|
+
|
|
475
|
+
```typescript
|
|
476
|
+
import {QRAccessService} from '@passgage/sdk-react-native';
|
|
477
|
+
|
|
478
|
+
const qrService = new QRAccessService(apiClient);
|
|
479
|
+
const entrance = await qrService.validateQRCode(qrCode, options);
|
|
137
480
|
```
|
|
138
481
|
|
|
139
|
-
|
|
482
|
+
#### `NFCAccessService`
|
|
140
483
|
|
|
141
|
-
|
|
484
|
+
```typescript
|
|
485
|
+
import {NFCAccessService} from '@passgage/sdk-react-native';
|
|
486
|
+
|
|
487
|
+
const nfcService = new NFCAccessService(apiClient);
|
|
488
|
+
const entrance = await nfcService.validateNFCCard(cardId, options);
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
#### `DeviceAccessService`
|
|
142
492
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
493
|
+
```typescript
|
|
494
|
+
import {DeviceAccessService} from '@passgage/sdk-react-native';
|
|
495
|
+
|
|
496
|
+
const deviceService = new DeviceAccessService(apiClient);
|
|
497
|
+
const device = await deviceService.getDeviceInfo(deviceId);
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
#### `LocationService`
|
|
501
|
+
|
|
502
|
+
```typescript
|
|
503
|
+
import {LocationService} from '@passgage/sdk-react-native';
|
|
504
|
+
|
|
505
|
+
const locationService = new LocationService();
|
|
506
|
+
const location = await locationService.getCurrentLocation();
|
|
507
|
+
const isInRange = locationService.isWithinRange(location, targetLocation, range);
|
|
508
|
+
```
|
|
149
509
|
|
|
150
510
|
### Components
|
|
151
511
|
|
|
152
512
|
- `<PassgageAccessProvider>` - SDK configuration provider
|
|
153
513
|
|
|
514
|
+
## Security Features
|
|
515
|
+
|
|
516
|
+
- **JWT Authentication**: Secure token-based authentication
|
|
517
|
+
- **Automatic Token Refresh**: Tokens are automatically refreshed when expired
|
|
518
|
+
- **Secure Storage**: Tokens stored securely using platform-specific secure storage (Keychain/Keystore)
|
|
519
|
+
- **TLS 1.2+**: All network communications encrypted
|
|
520
|
+
- **Location Verification**: Automatic location validation with range checking
|
|
521
|
+
- **Backend Logging**: Access logs sent to backend for audit trail
|
|
522
|
+
|
|
523
|
+
## Token Flow
|
|
524
|
+
|
|
525
|
+
The SDK automatically handles authentication tokens:
|
|
526
|
+
|
|
527
|
+
1. User logs in → JWT tokens stored securely
|
|
528
|
+
2. API client automatically adds `Authorization: Bearer {token}` to all requests
|
|
529
|
+
3. Tokens are automatically refreshed when they expire
|
|
530
|
+
4. On logout, all tokens are cleared
|
|
531
|
+
|
|
532
|
+
**You don't need to manually manage tokens!** Just provide msal tokens to provider and the SDK handles everything.
|
|
533
|
+
|
|
534
|
+
## Example Application
|
|
535
|
+
|
|
536
|
+
A complete example React Native app demonstrating all SDK features:
|
|
537
|
+
|
|
538
|
+
👉 [Passgage SDK Example App](https://github.com/passgage/passgage-sdk-example-react-native)
|
|
539
|
+
|
|
540
|
+
## TypeScript Support
|
|
541
|
+
|
|
542
|
+
The SDK is written in TypeScript and includes full type definitions for all APIs, hooks, and models.
|
|
543
|
+
|
|
154
544
|
## License
|
|
155
545
|
|
|
156
546
|
Proprietary - Passgage © 2025
|
|
547
|
+
|
|
548
|
+
## Support
|
|
549
|
+
|
|
550
|
+
For support, contact devops@passgage.com
|