@passgage/sdk-react-native 1.0.4 → 1.0.6
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 +409 -147
- package/dist/index.d.mts +166 -424
- package/dist/index.d.ts +166 -424
- package/dist/index.js +933 -1197
- package/dist/index.mjs +868 -1132
- package/package.json +8 -4
package/README.md
CHANGED
|
@@ -10,6 +10,15 @@ npm install @passgage/sdk-react-native
|
|
|
10
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:
|
|
@@ -19,9 +28,10 @@ npm install react-native-vision-camera react-native-nfc-manager @react-native-co
|
|
|
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)
|
|
25
35
|
- [react-native-keychain](https://github.com/oblador/react-native-keychain)
|
|
26
36
|
|
|
27
37
|
## Usage
|
|
@@ -31,232 +41,484 @@ Follow the installation instructions for each native library:
|
|
|
31
41
|
Wrap your app with `PassgageAccessProvider`:
|
|
32
42
|
|
|
33
43
|
```typescript
|
|
34
|
-
import {
|
|
44
|
+
import {PassgageAccessProvider} from '@passgage/sdk-react-native';
|
|
35
45
|
|
|
36
46
|
function App() {
|
|
37
47
|
return (
|
|
38
48
|
<PassgageAccessProvider
|
|
39
49
|
baseURL="https://your-api.passgage.com"
|
|
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 => {
|
|
41
55
|
// Handle session expiration
|
|
42
|
-
console.log('
|
|
56
|
+
console.log('Unauthorized error:', error);
|
|
43
57
|
}}
|
|
44
|
-
|
|
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
|
+
}}>
|
|
45
66
|
<YourApp />
|
|
46
67
|
</PassgageAccessProvider>
|
|
47
68
|
);
|
|
48
69
|
}
|
|
49
70
|
```
|
|
50
71
|
|
|
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
|
+
|
|
51
85
|
### Authentication
|
|
52
86
|
|
|
53
87
|
```typescript
|
|
54
|
-
|
|
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
|
+
};
|
|
55
98
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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);
|
|
60
132
|
},
|
|
61
|
-
|
|
62
|
-
|
|
133
|
+
onError: error => {
|
|
134
|
+
const errorMessage = error.message || 'QR validation failed';
|
|
135
|
+
Alert.alert('Failed', errorMessage);
|
|
63
136
|
},
|
|
64
137
|
});
|
|
65
138
|
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
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('');
|
|
71
155
|
};
|
|
72
156
|
|
|
73
157
|
return (
|
|
74
|
-
<View>
|
|
75
|
-
{
|
|
76
|
-
<Text
|
|
77
|
-
|
|
78
|
-
<
|
|
79
|
-
|
|
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>
|
|
80
212
|
</View>
|
|
81
213
|
);
|
|
82
|
-
}
|
|
214
|
+
};
|
|
83
215
|
```
|
|
84
216
|
|
|
85
|
-
###
|
|
217
|
+
### NFC Scanner
|
|
86
218
|
|
|
87
219
|
```typescript
|
|
88
|
-
|
|
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
|
+
});
|
|
89
237
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
console.log('QR scan successful:', entrance);
|
|
94
|
-
},
|
|
95
|
-
onError: (error) => {
|
|
96
|
-
console.error('QR scan failed:', error);
|
|
97
|
-
},
|
|
98
|
-
});
|
|
238
|
+
const handleStartScan = async () => {
|
|
239
|
+
startScanning();
|
|
240
|
+
};
|
|
99
241
|
|
|
242
|
+
const handleStopScan = async () => {
|
|
243
|
+
try {
|
|
244
|
+
await stopScanning();
|
|
245
|
+
} catch (error: any) {
|
|
246
|
+
console.error('Stop NFC scan error:', error);
|
|
247
|
+
}
|
|
248
|
+
};
|
|
100
249
|
return (
|
|
101
|
-
<View>
|
|
102
|
-
<
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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>
|
|
106
316
|
</View>
|
|
107
317
|
);
|
|
108
318
|
}
|
|
109
319
|
```
|
|
110
320
|
|
|
111
|
-
|
|
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.
|
|
112
330
|
|
|
113
331
|
```typescript
|
|
114
|
-
|
|
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)`
|
|
115
350
|
|
|
116
|
-
|
|
117
|
-
|
|
351
|
+
NFC card scanning with validation and location checking.
|
|
352
|
+
|
|
353
|
+
```typescript
|
|
354
|
+
const {nfcData, supportNFC, startScanning, stopScanning, isScanning, error} =
|
|
355
|
+
usePassgageNFCScanner({
|
|
356
|
+
options: {
|
|
357
|
+
skipLocationCheck: false,
|
|
358
|
+
skipRepetitiveCheck: false,
|
|
359
|
+
},
|
|
118
360
|
onSuccess: (entrance) => {
|
|
119
|
-
console.log('
|
|
361
|
+
console.log('Access granted:', entrance);
|
|
120
362
|
},
|
|
121
363
|
onError: (error) => {
|
|
122
|
-
console.error('
|
|
364
|
+
console.error('Access denied:', error);
|
|
123
365
|
},
|
|
124
366
|
});
|
|
125
367
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
return () => stopScanning();
|
|
129
|
-
}, []);
|
|
368
|
+
// Start NFC scanning
|
|
369
|
+
await startScanning();
|
|
130
370
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
<Text>{isScanning ? 'Scanning...' : 'Tap to scan'}</Text>
|
|
134
|
-
</View>
|
|
135
|
-
);
|
|
136
|
-
}
|
|
371
|
+
// Stop NFC scanning
|
|
372
|
+
await stopScanning();
|
|
137
373
|
```
|
|
138
374
|
|
|
139
|
-
|
|
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.
|
|
140
386
|
|
|
141
387
|
```typescript
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
function CheckInScreen() {
|
|
145
|
-
const { user } = usePassgageAuth();
|
|
146
|
-
const { getNearbyBranches, checkInEntry, checkInExit, isLoading } = usePassgageCheckIn();
|
|
147
|
-
const [branches, setBranches] = useState([]);
|
|
148
|
-
|
|
149
|
-
useEffect(() => {
|
|
150
|
-
const loadBranches = async () => {
|
|
151
|
-
const result = await getNearbyBranches({ radius: 5000 });
|
|
152
|
-
if (result.success) {
|
|
153
|
-
setBranches(result.data);
|
|
154
|
-
}
|
|
155
|
-
};
|
|
156
|
-
loadBranches();
|
|
157
|
-
}, []);
|
|
158
|
-
|
|
159
|
-
const handleCheckIn = async (branchId: string) => {
|
|
160
|
-
const result = await checkInEntry({
|
|
161
|
-
branchId,
|
|
162
|
-
userId: user.id,
|
|
163
|
-
});
|
|
388
|
+
const {location, error, refreshLocation} = useLocation();
|
|
164
389
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
}
|
|
168
|
-
};
|
|
390
|
+
// Get current location
|
|
391
|
+
console.log(location?.latitude, location?.longitude);
|
|
169
392
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
{branches.map((branch) => (
|
|
173
|
-
<TouchableOpacity key={branch.id} onPress={() => handleCheckIn(branch.id)}>
|
|
174
|
-
<Text>{branch.title}</Text>
|
|
175
|
-
</TouchableOpacity>
|
|
176
|
-
))}
|
|
177
|
-
</View>
|
|
178
|
-
);
|
|
179
|
-
}
|
|
393
|
+
// Manually refresh location
|
|
394
|
+
await refreshLocation();
|
|
180
395
|
```
|
|
181
396
|
|
|
182
|
-
|
|
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.
|
|
183
407
|
|
|
184
408
|
```typescript
|
|
185
|
-
import {
|
|
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);
|
|
430
|
+
}
|
|
186
431
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
const { logEntry, logExit, isLoading } = usePassgageRemoteWork();
|
|
432
|
+
// Logout
|
|
433
|
+
await logout();
|
|
190
434
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
userId: user.id,
|
|
194
|
-
description: 'Working from home',
|
|
195
|
-
});
|
|
435
|
+
// Restore previous session
|
|
436
|
+
await restoreAuth();
|
|
196
437
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
}
|
|
200
|
-
};
|
|
438
|
+
// Manually refresh token
|
|
439
|
+
await refreshToken();
|
|
201
440
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
description: 'Finished work',
|
|
206
|
-
});
|
|
441
|
+
// Clear error state
|
|
442
|
+
clearError();
|
|
443
|
+
```
|
|
207
444
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
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
|
|
212
450
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
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`
|
|
464
|
+
|
|
465
|
+
```typescript
|
|
466
|
+
import {AuthService} from '@passgage/sdk-react-native';
|
|
467
|
+
|
|
468
|
+
const authService = new AuthService(apiClient);
|
|
469
|
+
await authService.loginWithAzure({id_token: 'token'});
|
|
470
|
+
await authService.logout();
|
|
220
471
|
```
|
|
221
472
|
|
|
222
|
-
|
|
473
|
+
#### `QRAccessService`
|
|
223
474
|
|
|
224
|
-
|
|
475
|
+
```typescript
|
|
476
|
+
import {QRAccessService} from '@passgage/sdk-react-native';
|
|
225
477
|
|
|
226
|
-
|
|
478
|
+
const qrService = new QRAccessService(apiClient);
|
|
479
|
+
const entrance = await qrService.validateQRCode(qrCode, options);
|
|
480
|
+
```
|
|
227
481
|
|
|
228
|
-
|
|
229
|
-
- `usePassgageQRScanner()` - QR code scanning with validation
|
|
230
|
-
- `usePassgageNFCScanner()` - NFC card scanning with validation
|
|
231
|
-
- `usePassgageCheckIn()` - GPS-based check-in with nearby branches
|
|
232
|
-
- `usePassgageRemoteWork()` - Remote work entry/exit logging
|
|
233
|
-
- `useLocation()` - Location tracking and permissions
|
|
234
|
-
- `usePassgageAccess()` - Access SDK context and services
|
|
482
|
+
#### `NFCAccessService`
|
|
235
483
|
|
|
236
|
-
|
|
484
|
+
```typescript
|
|
485
|
+
import {NFCAccessService} from '@passgage/sdk-react-native';
|
|
237
486
|
|
|
238
|
-
|
|
487
|
+
const nfcService = new NFCAccessService(apiClient);
|
|
488
|
+
const entrance = await nfcService.validateNFCCard(cardId, options);
|
|
489
|
+
```
|
|
239
490
|
|
|
240
|
-
|
|
491
|
+
#### `DeviceAccessService`
|
|
241
492
|
|
|
242
|
-
|
|
493
|
+
```typescript
|
|
494
|
+
import {DeviceAccessService} from '@passgage/sdk-react-native';
|
|
243
495
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
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
|
+
```
|
|
509
|
+
|
|
510
|
+
### Components
|
|
511
|
+
|
|
512
|
+
- `<PassgageAccessProvider>` - SDK configuration provider
|
|
251
513
|
|
|
252
514
|
## Security Features
|
|
253
515
|
|
|
254
516
|
- **JWT Authentication**: Secure token-based authentication
|
|
255
517
|
- **Automatic Token Refresh**: Tokens are automatically refreshed when expired
|
|
256
|
-
- **Secure Storage**: Tokens stored securely using platform-specific secure storage
|
|
518
|
+
- **Secure Storage**: Tokens stored securely using platform-specific secure storage (Keychain/Keystore)
|
|
257
519
|
- **TLS 1.2+**: All network communications encrypted
|
|
258
520
|
- **Location Verification**: Automatic location validation with range checking
|
|
259
|
-
- **
|
|
521
|
+
- **Backend Logging**: Access logs sent to backend for audit trail
|
|
260
522
|
|
|
261
523
|
## Token Flow
|
|
262
524
|
|
|
@@ -267,7 +529,7 @@ The SDK automatically handles authentication tokens:
|
|
|
267
529
|
3. Tokens are automatically refreshed when they expire
|
|
268
530
|
4. On logout, all tokens are cleared
|
|
269
531
|
|
|
270
|
-
**You don't need to manually manage tokens!** Just
|
|
532
|
+
**You don't need to manually manage tokens!** Just provide msal tokens to provider and the SDK handles everything.
|
|
271
533
|
|
|
272
534
|
## Example Application
|
|
273
535
|
|