@pelican-identity/react-native 1.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 +483 -0
- package/dist/index.d.mts +16 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +191 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +185 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +48 -0
package/README.md
ADDED
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
# Pelican Identity React Native SDK
|
|
2
|
+
|
|
3
|
+
React Native SDK for Pelican authentication and identity verification
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
### Using npm
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @pelican-identity/react-native react-native-get-random-values
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
### Using yarn
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
yarn add @pelican-identity/react-native react-native-get-random-values
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### Using pnpm
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pnpm add @pelican-identity/react-native react-native-get-random-values
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Required Setup
|
|
29
|
+
|
|
30
|
+
### 1. Set your app’s bundle identifier
|
|
31
|
+
|
|
32
|
+
You must configure your bundle identifier in your app and pass it explicitly to Pelican.
|
|
33
|
+
**Expo (`app.json` or `app.config.js`):**
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"expo": {
|
|
38
|
+
"name": "MyApp",
|
|
39
|
+
"ios": {
|
|
40
|
+
"bundleIdentifier": "com.yourcompany.myapp"
|
|
41
|
+
},
|
|
42
|
+
"android": {
|
|
43
|
+
"package": "com.yourcompany.myapp"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**Bare React Native:**
|
|
50
|
+
|
|
51
|
+
- **iOS:** Xcode → Target → General → Bundle Identifier
|
|
52
|
+
- **Android:** `android/app/build.gradle` → `applicationId`
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
### 2. Import crypto polyfill (Important)
|
|
57
|
+
|
|
58
|
+
In your app’s entry file (`App.tsx`, `index.js`, or `layout.tsx`), import this **before anything else**:
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
import "react-native-get-random-values"; // Must be first
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
### 3. Whitelist your app in Pelican Dashboard
|
|
67
|
+
|
|
68
|
+
Add your app’s bundle identifier (example: `com.yourcompany.myapp`) to your project’s whitelist in the Pelican dashboard.
|
|
69
|
+
|
|
70
|
+
Pelican validates **explicit app identity** on every authentication attempt.
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
### 4. Callback URL Configuration
|
|
75
|
+
|
|
76
|
+
The `callBackUrl` defines where the Pelican vault app redirects after authentication, this must be the same page/screen where you have initialized the SDK, if this is not provided, SDK will attempt to authenticate **ONCE** when the app is reopened.
|
|
77
|
+
|
|
78
|
+
### Expo Go (development)
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
callBackUrl = "exp://192.168.1.100:8081";
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Use your **local IP** and Expo dev server port.
|
|
85
|
+
|
|
86
|
+
### Expo standalone builds
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
callBackUrl = "myapp://auth-callback";
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
In `app.json`:
|
|
93
|
+
|
|
94
|
+
```json
|
|
95
|
+
{
|
|
96
|
+
"expo": {
|
|
97
|
+
"scheme": "myapp"
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Bare React Native
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
callBackUrl = "myapp://auth-callback";
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Configure deep linking:
|
|
109
|
+
|
|
110
|
+
- **iOS:** Xcode → Info → URL Types
|
|
111
|
+
- **Android:** `AndroidManifest.xml` intent filter
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Usage
|
|
116
|
+
|
|
117
|
+
### Basic Usage
|
|
118
|
+
|
|
119
|
+
```tsx
|
|
120
|
+
import React from "react";
|
|
121
|
+
import { View } from "react-native";
|
|
122
|
+
import { PelicanAuth } from "@pelican-identity/react-native";
|
|
123
|
+
|
|
124
|
+
export default function App() {
|
|
125
|
+
return (
|
|
126
|
+
<View style={{ flex: 1 }}>
|
|
127
|
+
<PelicanAuth
|
|
128
|
+
publicKey="your-business-public-key"
|
|
129
|
+
projectId="your-project-id"
|
|
130
|
+
appId="com.yourcompany.myapp"
|
|
131
|
+
authType="signup"
|
|
132
|
+
callBackUrl="myapp://auth-callback"
|
|
133
|
+
onSuccess={(data) => {
|
|
134
|
+
console.log("Authentication successful:", data);
|
|
135
|
+
}}
|
|
136
|
+
onError={(error) => {
|
|
137
|
+
console.error("Authentication failed:", error);
|
|
138
|
+
}}
|
|
139
|
+
/>
|
|
140
|
+
</View>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Complete Example
|
|
148
|
+
|
|
149
|
+
```tsx
|
|
150
|
+
import React, { useState } from "react";
|
|
151
|
+
import { View, Text, StyleSheet } from "react-native";
|
|
152
|
+
import { PelicanAuth } from "@pelican-identity/react-native";
|
|
153
|
+
|
|
154
|
+
export default function LoginScreen() {
|
|
155
|
+
const [user, setUser] = useState(null);
|
|
156
|
+
const [error, setError] = useState<string | null>(null);
|
|
157
|
+
|
|
158
|
+
return (
|
|
159
|
+
<View style={styles.container}>
|
|
160
|
+
<Text style={styles.title}>Login to MyApp</Text>
|
|
161
|
+
|
|
162
|
+
<PelicanAuth
|
|
163
|
+
publicKey="your-business-public-key"
|
|
164
|
+
projectId="your-project-id"
|
|
165
|
+
appId="com.yourcompany.myapp"
|
|
166
|
+
authType="login"
|
|
167
|
+
callBackUrl="myapp://auth-callback"
|
|
168
|
+
onSuccess={(data) => {
|
|
169
|
+
setUser(data);
|
|
170
|
+
// Send user data to your backend
|
|
171
|
+
setError(null);
|
|
172
|
+
}}
|
|
173
|
+
onError={(err) => {
|
|
174
|
+
setError(err.message);
|
|
175
|
+
setUser(null);
|
|
176
|
+
}}
|
|
177
|
+
/>
|
|
178
|
+
|
|
179
|
+
{user && <Text style={styles.success}>Authenticated successfully</Text>}
|
|
180
|
+
|
|
181
|
+
{error && <Text style={styles.error}>{error}</Text>}
|
|
182
|
+
</View>
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const styles = StyleSheet.create({
|
|
187
|
+
container: { flex: 1, justifyContent: "center", alignItems: "center" },
|
|
188
|
+
title: { fontSize: 22, fontWeight: "bold", marginBottom: 20 },
|
|
189
|
+
success: { color: "green", marginTop: 20 },
|
|
190
|
+
error: { color: "red", marginTop: 20 },
|
|
191
|
+
});
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## API Reference
|
|
197
|
+
|
|
198
|
+
### `PelicanAuth`
|
|
199
|
+
|
|
200
|
+
Main authentication component.
|
|
201
|
+
|
|
202
|
+
#### Props
|
|
203
|
+
|
|
204
|
+
| Prop | Type | Required | Description |
|
|
205
|
+
| ----------------- | ------------------------------------------ | -------- | --------------------------------------------------- |
|
|
206
|
+
| `publicKey` | `string` | ✅ | Business public key from Pelican dashboard |
|
|
207
|
+
| `projectId` | `string` | ✅ | Project ID from Pelican dashboard |
|
|
208
|
+
| `appId` | `string` | ✅ | App bundle identifier |
|
|
209
|
+
| `authType` | `"signup" \| "login" \| "id-verification"` | ✅ | Authentication flow |
|
|
210
|
+
| `onSuccess` | `(data: IdentityResult) => void` | ✅ | Success callback containing authenticated user data |
|
|
211
|
+
| `callBackUrl` | `string` | Optional | Deeplink to return to app |
|
|
212
|
+
| `onError` | `(error) => void` | Optional | Error callback |
|
|
213
|
+
| `onLoading` | `(loading: boolean) => void` | Optional | Loading callback, useful for loading stares |
|
|
214
|
+
| `buttonComponent` | `ReactNode` | Optional | Custom trigger UI |
|
|
215
|
+
|
|
216
|
+
> If `appId` is missing or does not match the whitelisted value, Pelican will fail immediately.
|
|
217
|
+
|
|
218
|
+
### Best practice
|
|
219
|
+
|
|
220
|
+
Fetch your Pelican configuration (public key, project ID) from your backend at runtime instead of hard-coding them into your mobile app.
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
# Authentication Response
|
|
225
|
+
|
|
226
|
+
When authentication completes successfully, Pelican returns a structured **identity result** to your application via the `onSuccess` callback.
|
|
227
|
+
|
|
228
|
+
This response contains a **deterministic user identifier** for your business, along with optional verified user data and identity verification (KYC) information depending on the authentication flow used.
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## Success Callback
|
|
233
|
+
|
|
234
|
+
```ts
|
|
235
|
+
onSuccess: (result: IdentityResult) => void;
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## IdentityResult
|
|
241
|
+
|
|
242
|
+
```ts
|
|
243
|
+
interface IdentityResult {
|
|
244
|
+
/** Deterministic unique user identifier specific to your business */
|
|
245
|
+
user_id: string;
|
|
246
|
+
|
|
247
|
+
/** Basic user profile and contact information (if available) */
|
|
248
|
+
user_data?: IUserData;
|
|
249
|
+
|
|
250
|
+
/** Identity document and liveness verification data */
|
|
251
|
+
id_verification: IKycData;
|
|
252
|
+
|
|
253
|
+
/** Download URLs for verified identity documents */
|
|
254
|
+
id_downloadurls?: {
|
|
255
|
+
front_of_card?: string;
|
|
256
|
+
back_of_card?: string;
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
## `user_id`
|
|
264
|
+
|
|
265
|
+
- A **stable, deterministic identifier** generated by Pelican.
|
|
266
|
+
- Unique **per business**, not global.
|
|
267
|
+
- Safe to store and use as your internal user reference.
|
|
268
|
+
- Does **not** expose personally identifiable information (PII).
|
|
269
|
+
|
|
270
|
+
> This identifier remains the same for the same user across future authentications within your business.
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## User Data (`user_data`)
|
|
275
|
+
|
|
276
|
+
Returned when available and permitted by the authentication flow.
|
|
277
|
+
|
|
278
|
+
```ts
|
|
279
|
+
interface IUserData {
|
|
280
|
+
first_name?: string;
|
|
281
|
+
last_name?: string;
|
|
282
|
+
other_names?: string;
|
|
283
|
+
email?: IEmail;
|
|
284
|
+
phone?: IPhone;
|
|
285
|
+
dob?: string | Date;
|
|
286
|
+
gender?: "male" | "female" | "other";
|
|
287
|
+
country?: string;
|
|
288
|
+
state?: string;
|
|
289
|
+
city?: string;
|
|
290
|
+
address?: string;
|
|
291
|
+
occupation?: string;
|
|
292
|
+
company?: string;
|
|
293
|
+
website?: string;
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### Email
|
|
298
|
+
|
|
299
|
+
```ts
|
|
300
|
+
interface IEmail {
|
|
301
|
+
id: number;
|
|
302
|
+
value: string;
|
|
303
|
+
verifiedAt: string;
|
|
304
|
+
verificationProvider: string;
|
|
305
|
+
}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### Phone
|
|
309
|
+
|
|
310
|
+
```ts
|
|
311
|
+
interface IPhone {
|
|
312
|
+
id: number;
|
|
313
|
+
country: string;
|
|
314
|
+
callingCode: string;
|
|
315
|
+
number: string;
|
|
316
|
+
verifiedAt: string;
|
|
317
|
+
verificationProvider: string;
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
All contact data returned by Pelican is **verified** and includes the service used for verification.
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
## Identity Verification (`id_verification`)
|
|
326
|
+
|
|
327
|
+
Returned for flows involving identity verification or KYC.
|
|
328
|
+
|
|
329
|
+
```ts
|
|
330
|
+
interface IKycData {
|
|
331
|
+
id: number;
|
|
332
|
+
status?: "Approved" | "Declined" | "In Review";
|
|
333
|
+
document_type?:
|
|
334
|
+
| "national id card"
|
|
335
|
+
| "passport"
|
|
336
|
+
| "driver's license"
|
|
337
|
+
| "residence permit";
|
|
338
|
+
document_number?: string;
|
|
339
|
+
personal_number?: string;
|
|
340
|
+
date_of_birth?: string | Date;
|
|
341
|
+
age?: number;
|
|
342
|
+
expiration_date?: string | Date;
|
|
343
|
+
date_of_issue?: string | Date;
|
|
344
|
+
issuing_state?: string;
|
|
345
|
+
issuing_state_name?: string;
|
|
346
|
+
first_name?: string;
|
|
347
|
+
last_name?: string;
|
|
348
|
+
full_name?: string;
|
|
349
|
+
gender?: string;
|
|
350
|
+
address?: string;
|
|
351
|
+
formatted_address?: string;
|
|
352
|
+
nationality?: string;
|
|
353
|
+
liveness_percentage?: number;
|
|
354
|
+
face_match_percentage?: number;
|
|
355
|
+
verified_at?: string | Date;
|
|
356
|
+
}
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
### Verification Scores
|
|
360
|
+
|
|
361
|
+
- `liveness_percentage`: Confidence score (0–100) from face liveness detection.
|
|
362
|
+
- `face_match_percentage`: Confidence score (0–100) matching selfie to document photo.
|
|
363
|
+
|
|
364
|
+
---
|
|
365
|
+
|
|
366
|
+
## Document Downloads
|
|
367
|
+
|
|
368
|
+
If document access is enabled for your project (NB: your business must be verified and KYB completed), Pelican provides secure download URLs valid for 24 hours as Pelican does not store documents and only provides access to them from the Pelican vault for a limited time. You are required to download and store the documents on your backend for compliance and security reasons according to your local data protection regulations:
|
|
369
|
+
|
|
370
|
+
```ts
|
|
371
|
+
id_downloadurls?: {
|
|
372
|
+
front_of_card?: string;
|
|
373
|
+
back_of_card?: string;
|
|
374
|
+
};
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
These URLs are:
|
|
378
|
+
|
|
379
|
+
- Short-lived
|
|
380
|
+
- Access-controlled
|
|
381
|
+
- Intended for secure backend or compliance workflows
|
|
382
|
+
|
|
383
|
+
---
|
|
384
|
+
|
|
385
|
+
## Authentication Flow Differences
|
|
386
|
+
|
|
387
|
+
| Auth Type | Returned Data |
|
|
388
|
+
| ----------------- | ------------------------------------------- |
|
|
389
|
+
| `signup` | `user_id`, basic `user_data` |
|
|
390
|
+
| `login` | `user_id`, previously generated at signup |
|
|
391
|
+
| `id-verification` | `user_id`, `id_verification`, document URLs |
|
|
392
|
+
|
|
393
|
+
Returned fields depend on:
|
|
394
|
+
|
|
395
|
+
- User consent
|
|
396
|
+
- Project configuration
|
|
397
|
+
- Regulatory requirements
|
|
398
|
+
|
|
399
|
+
---
|
|
400
|
+
|
|
401
|
+
## Troubleshooting
|
|
402
|
+
|
|
403
|
+
### Missing app identifier.
|
|
404
|
+
|
|
405
|
+
- Ensure you passed the `appId` prop
|
|
406
|
+
- Ensure it matches the bundle identifier configured in your app
|
|
407
|
+
|
|
408
|
+
### No whitelisted app IDs found
|
|
409
|
+
|
|
410
|
+
- Add the `appId` to your Pelican project whitelist
|
|
411
|
+
- Ensure there are no typos or environment mismatches
|
|
412
|
+
|
|
413
|
+
### “crypto.getRandomValues() not supported”
|
|
414
|
+
|
|
415
|
+
- Ensure `react-native-get-random-values` is imported **before anything else**
|
|
416
|
+
|
|
417
|
+
### Redirect doesn’t return to app
|
|
418
|
+
|
|
419
|
+
- Verify `callBackUrl` scheme matches your app configuration
|
|
420
|
+
- Confirm deep linking is correctly set up
|
|
421
|
+
|
|
422
|
+
### Unsupported authentication type
|
|
423
|
+
|
|
424
|
+
- Ensure you passed a valid `authType` prop
|
|
425
|
+
|
|
426
|
+
### Billing issues, please contact this site owner
|
|
427
|
+
|
|
428
|
+
- Ensure your business wallet has sufficient balance
|
|
429
|
+
- Pelican attempts to send a low balance alert to your email, you can configure the threshold in the Pelican dashboard
|
|
430
|
+
|
|
431
|
+
### Project not found
|
|
432
|
+
|
|
433
|
+
- Ensure you passed a valid `projectId` prop
|
|
434
|
+
- Ensure it matches the project ID configured in your app
|
|
435
|
+
|
|
436
|
+
### Business not found
|
|
437
|
+
|
|
438
|
+
- Ensure you passed a valid `publicKey` prop
|
|
439
|
+
- Ensure it matches the business ID configured in your app
|
|
440
|
+
|
|
441
|
+
---
|
|
442
|
+
|
|
443
|
+
## Error Handling
|
|
444
|
+
|
|
445
|
+
If authentication fails, the `onError` callback is invoked:
|
|
446
|
+
|
|
447
|
+
```ts
|
|
448
|
+
onError?: (error: Error) => void;
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
Common error scenarios include:
|
|
452
|
+
|
|
453
|
+
- User cancellation
|
|
454
|
+
- Network failure
|
|
455
|
+
- Invalid project configuration
|
|
456
|
+
- Session expiration
|
|
457
|
+
- Invalid authentication type
|
|
458
|
+
- Billing issues
|
|
459
|
+
|
|
460
|
+
---
|
|
461
|
+
|
|
462
|
+
## Security Notes
|
|
463
|
+
|
|
464
|
+
- All identity data is transmitted **encrypted**.
|
|
465
|
+
- Pelican never exposes raw credentials.
|
|
466
|
+
- Identifiers are scoped per business.
|
|
467
|
+
- Mobile identifiers (App ID / Bundle ID) are used for **application identification**.
|
|
468
|
+
|
|
469
|
+
---
|
|
470
|
+
|
|
471
|
+
### Final note
|
|
472
|
+
|
|
473
|
+
Pelican deliberately separates:
|
|
474
|
+
|
|
475
|
+
- **Identity** (who the user is)
|
|
476
|
+
- **Authentication** (this session)
|
|
477
|
+
- **Verification** (how confident we are)
|
|
478
|
+
|
|
479
|
+
This keeps your app flexible, compliant, and secure by default.
|
|
480
|
+
|
|
481
|
+
## License
|
|
482
|
+
|
|
483
|
+
MIT
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { PelicanAuthConfig, IdentityResult } from '@pelican-identity/auth-core';
|
|
3
|
+
export * from '@pelican-identity/auth-core';
|
|
4
|
+
|
|
5
|
+
interface PelicanRNAuthProps extends PelicanAuthConfig {
|
|
6
|
+
appId: string;
|
|
7
|
+
onSuccess: (result: IdentityResult) => void;
|
|
8
|
+
onError?: (error: Error) => void;
|
|
9
|
+
onLoading?: (loading: boolean) => void;
|
|
10
|
+
buttonComponent?: React.ReactNode;
|
|
11
|
+
callBackUrl?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
declare const PelicanAuth: ({ authType, projectId, publicKey, onSuccess, callBackUrl, onError, buttonComponent, onLoading, appId, }: PelicanRNAuthProps) => react_jsx_runtime.JSX.Element;
|
|
15
|
+
|
|
16
|
+
export { PelicanAuth };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { PelicanAuthConfig, IdentityResult } from '@pelican-identity/auth-core';
|
|
3
|
+
export * from '@pelican-identity/auth-core';
|
|
4
|
+
|
|
5
|
+
interface PelicanRNAuthProps extends PelicanAuthConfig {
|
|
6
|
+
appId: string;
|
|
7
|
+
onSuccess: (result: IdentityResult) => void;
|
|
8
|
+
onError?: (error: Error) => void;
|
|
9
|
+
onLoading?: (loading: boolean) => void;
|
|
10
|
+
buttonComponent?: React.ReactNode;
|
|
11
|
+
callBackUrl?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
declare const PelicanAuth: ({ authType, projectId, publicKey, onSuccess, callBackUrl, onError, buttonComponent, onLoading, appId, }: PelicanRNAuthProps) => react_jsx_runtime.JSX.Element;
|
|
15
|
+
|
|
16
|
+
export { PelicanAuth };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var React = require('react');
|
|
4
|
+
var reactNative = require('react-native');
|
|
5
|
+
require('react-native-get-random-values');
|
|
6
|
+
var authCore = require('@pelican-identity/auth-core');
|
|
7
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
8
|
+
|
|
9
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
10
|
+
|
|
11
|
+
var React__default = /*#__PURE__*/_interopDefault(React);
|
|
12
|
+
|
|
13
|
+
// src/components/PelicanAuth.tsx
|
|
14
|
+
var getRelayUrl = async ({
|
|
15
|
+
businessKey,
|
|
16
|
+
authType,
|
|
17
|
+
projectID,
|
|
18
|
+
appId
|
|
19
|
+
}) => {
|
|
20
|
+
if (!appId) {
|
|
21
|
+
throw new Error("App ID is required");
|
|
22
|
+
}
|
|
23
|
+
const headers = {
|
|
24
|
+
"Content-Type": "application/json",
|
|
25
|
+
"X-App-ID": appId
|
|
26
|
+
};
|
|
27
|
+
const response = await fetch(
|
|
28
|
+
`${authCore.BASEURL}/relay?public_key=${businessKey}&auth_type=${authType}&project_id=${projectID}`,
|
|
29
|
+
{ headers }
|
|
30
|
+
);
|
|
31
|
+
if (!response.ok) {
|
|
32
|
+
const error = await response.text();
|
|
33
|
+
throw new Error(`Failed to initiate authentication: ${error}`);
|
|
34
|
+
}
|
|
35
|
+
return response.json();
|
|
36
|
+
};
|
|
37
|
+
var getEncryptedData = async ({
|
|
38
|
+
businessKey,
|
|
39
|
+
authType,
|
|
40
|
+
projectID,
|
|
41
|
+
sessionID,
|
|
42
|
+
appId
|
|
43
|
+
}) => {
|
|
44
|
+
if (!appId) {
|
|
45
|
+
throw new Error("App ID is required");
|
|
46
|
+
}
|
|
47
|
+
const headers = {
|
|
48
|
+
"Content-Type": "application/json",
|
|
49
|
+
"X-App-ID": appId
|
|
50
|
+
};
|
|
51
|
+
const response = await fetch(
|
|
52
|
+
`${authCore.BASEURL}/get-rn-sdk-encrypted-data?public_key=${businessKey}&auth_type=${authType}&project_id=${projectID}&session_id=${sessionID}`,
|
|
53
|
+
{ headers }
|
|
54
|
+
);
|
|
55
|
+
if (!response.ok) {
|
|
56
|
+
const error = await response.text();
|
|
57
|
+
throw new Error(`Failed to get encrypted data: ${error}`);
|
|
58
|
+
}
|
|
59
|
+
return response.json();
|
|
60
|
+
};
|
|
61
|
+
var cryptoService = new authCore.CryptoService();
|
|
62
|
+
var PelicanAuth = ({
|
|
63
|
+
authType,
|
|
64
|
+
projectId,
|
|
65
|
+
publicKey,
|
|
66
|
+
onSuccess,
|
|
67
|
+
callBackUrl,
|
|
68
|
+
onError,
|
|
69
|
+
buttonComponent,
|
|
70
|
+
onLoading,
|
|
71
|
+
appId
|
|
72
|
+
}) => {
|
|
73
|
+
const sessionKey = React__default.default.useRef(null);
|
|
74
|
+
const sessionID = React__default.default.useRef(null);
|
|
75
|
+
const [loading, setLoading] = React.useState(false);
|
|
76
|
+
const initialize = async () => {
|
|
77
|
+
try {
|
|
78
|
+
setLoading(true);
|
|
79
|
+
onLoading?.(true);
|
|
80
|
+
const { relay_url, session_id } = await getRelayUrl({
|
|
81
|
+
businessKey: publicKey,
|
|
82
|
+
authType,
|
|
83
|
+
projectID: projectId,
|
|
84
|
+
appId
|
|
85
|
+
});
|
|
86
|
+
if (!relay_url) {
|
|
87
|
+
throw new Error("Failed to get relay URL");
|
|
88
|
+
}
|
|
89
|
+
sessionKey.current = cryptoService.generateSymmetricKey();
|
|
90
|
+
sessionID.current = session_id;
|
|
91
|
+
const buildUrl = `${relay_url}?sessionKey=${encodeURIComponent(
|
|
92
|
+
sessionKey.current || ""
|
|
93
|
+
)}&sessionID=${encodeURIComponent(
|
|
94
|
+
sessionID.current || ""
|
|
95
|
+
)}&authType=${encodeURIComponent(
|
|
96
|
+
authType
|
|
97
|
+
)}&projectId=${encodeURIComponent(
|
|
98
|
+
projectId
|
|
99
|
+
)}&publicKey=${encodeURIComponent(
|
|
100
|
+
publicKey
|
|
101
|
+
)}&callBackUrl=${encodeURIComponent(callBackUrl || "")}`;
|
|
102
|
+
reactNative.Linking.openURL(buildUrl);
|
|
103
|
+
} catch (error) {
|
|
104
|
+
console.log(error);
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
const handleCallback = async () => {
|
|
108
|
+
try {
|
|
109
|
+
if (!sessionID.current) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
if (!sessionKey.current) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
const { cipher, nonce } = await getEncryptedData({
|
|
116
|
+
businessKey: publicKey,
|
|
117
|
+
authType,
|
|
118
|
+
projectID: projectId,
|
|
119
|
+
sessionID: sessionID.current,
|
|
120
|
+
appId
|
|
121
|
+
});
|
|
122
|
+
const decryptedData = cryptoService.decryptSymmetric({
|
|
123
|
+
encrypted: { cipher, nonce },
|
|
124
|
+
keyString: sessionKey.current
|
|
125
|
+
});
|
|
126
|
+
if (decryptedData) {
|
|
127
|
+
const result = JSON.parse(decryptedData);
|
|
128
|
+
onSuccess(result);
|
|
129
|
+
sessionKey.current = null;
|
|
130
|
+
sessionID.current = null;
|
|
131
|
+
setLoading(false);
|
|
132
|
+
onLoading?.(false);
|
|
133
|
+
} else {
|
|
134
|
+
onError?.(new Error("Failed to get identity result"));
|
|
135
|
+
sessionKey.current = null;
|
|
136
|
+
sessionID.current = null;
|
|
137
|
+
setLoading(false);
|
|
138
|
+
onLoading?.(false);
|
|
139
|
+
}
|
|
140
|
+
} catch (error) {
|
|
141
|
+
console.log(error);
|
|
142
|
+
onError?.(error);
|
|
143
|
+
sessionKey.current = null;
|
|
144
|
+
sessionID.current = null;
|
|
145
|
+
setLoading(false);
|
|
146
|
+
onLoading?.(false);
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
React__default.default.useEffect(() => {
|
|
150
|
+
const subscription = reactNative.AppState.addEventListener("change", (nextAppState) => {
|
|
151
|
+
if (nextAppState === "active") {
|
|
152
|
+
handleCallback();
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
return () => {
|
|
156
|
+
subscription.remove();
|
|
157
|
+
};
|
|
158
|
+
}, []);
|
|
159
|
+
return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.TouchableOpacity, { onPress: initialize, disabled: loading, children: buttonComponent || /* @__PURE__ */ jsxRuntime.jsxs(
|
|
160
|
+
reactNative.View,
|
|
161
|
+
{
|
|
162
|
+
style: {
|
|
163
|
+
flexDirection: "row",
|
|
164
|
+
alignItems: "center",
|
|
165
|
+
gap: 10,
|
|
166
|
+
paddingHorizontal: 10,
|
|
167
|
+
paddingVertical: 10,
|
|
168
|
+
borderRadius: 20,
|
|
169
|
+
borderWidth: 1
|
|
170
|
+
},
|
|
171
|
+
children: [
|
|
172
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
173
|
+
reactNative.Image,
|
|
174
|
+
{
|
|
175
|
+
source: {
|
|
176
|
+
uri: "https://res.cloudinary.com/de0jr8mcm/image/upload/v1765904735/pelican/pelican_icon_r9ghqw.png"
|
|
177
|
+
},
|
|
178
|
+
style: { width: 30, height: 30 }
|
|
179
|
+
}
|
|
180
|
+
),
|
|
181
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { fontSize: 16, fontWeight: "600" }, children: authType === "login" ? "Login with Pelican" : authType === "signup" ? "Signup with Pelican" : authType === "id-verification" ? "Verify identity with Pelican" : "Authenticate with Pelican" }),
|
|
182
|
+
loading && /* @__PURE__ */ jsxRuntime.jsx(reactNative.ActivityIndicator, { color: "#000" })
|
|
183
|
+
]
|
|
184
|
+
}
|
|
185
|
+
) }) });
|
|
186
|
+
};
|
|
187
|
+
var PelicanAuth_default = PelicanAuth;
|
|
188
|
+
|
|
189
|
+
exports.PelicanAuth = PelicanAuth_default;
|
|
190
|
+
//# sourceMappingURL=index.js.map
|
|
191
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utilities.ts","../src/components/PelicanAuth.tsx"],"names":["BASEURL","CryptoService","React","useState","Linking","AppState","jsx","View","TouchableOpacity","jsxs","Image","Text","ActivityIndicator"],"mappings":"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","file":"index.js","sourcesContent":["import \"react-native-get-random-values\";\n\nimport { BASEURL, type AuthType } from \"@pelican-identity/auth-core\";\n\nexport const getRelayUrl = async ({\n businessKey,\n authType,\n projectID,\n appId,\n}: {\n businessKey: string;\n authType: AuthType;\n projectID: string;\n appId: string;\n}) => {\n if (!appId) {\n throw new Error(\"App ID is required\");\n }\n const headers = {\n \"Content-Type\": \"application/json\",\n \"X-App-ID\": appId,\n };\n\n const response = await fetch(\n `${BASEURL}/relay?public_key=${businessKey}&auth_type=${authType}&project_id=${projectID}`,\n { headers }\n );\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`Failed to initiate authentication: ${error}`);\n }\n\n return response.json() as Promise<{ relay_url: string; session_id: string }>;\n};\n\nexport const getEncryptedData = async ({\n businessKey,\n authType,\n projectID,\n sessionID,\n appId,\n}: {\n businessKey: string;\n authType: AuthType;\n projectID: string;\n sessionID: string;\n appId: string;\n}) => {\n if (!appId) {\n throw new Error(\"App ID is required\");\n }\n const headers = {\n \"Content-Type\": \"application/json\",\n \"X-App-ID\": appId,\n };\n\n const response = await fetch(\n `${BASEURL}/get-rn-sdk-encrypted-data?public_key=${businessKey}&auth_type=${authType}&project_id=${projectID}&session_id=${sessionID}`,\n { headers }\n );\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`Failed to get encrypted data: ${error}`);\n }\n\n return response.json() as Promise<{ cipher: string; nonce: string }>;\n};\n","import React, { useState } from \"react\";\nimport {\n ActivityIndicator,\n AppState,\n Image,\n Linking,\n Text,\n TouchableOpacity,\n View,\n} from \"react-native\";\nimport \"react-native-get-random-values\";\n\nimport { getEncryptedData, getRelayUrl } from \"../utilities\";\nimport { PelicanRNAuthProps } from \"../types\";\nimport { IdentityResult, CryptoService } from \"@pelican-identity/auth-core\";\n\nconst cryptoService = new CryptoService();\nconst PelicanAuth = ({\n authType,\n projectId,\n publicKey,\n onSuccess,\n callBackUrl,\n onError,\n buttonComponent,\n onLoading,\n appId,\n}: PelicanRNAuthProps) => {\n const sessionKey = React.useRef<string | null>(null);\n const sessionID = React.useRef<string | null>(null);\n const [loading, setLoading] = useState(false);\n\n const initialize = async () => {\n try {\n setLoading(true);\n onLoading?.(true);\n const { relay_url, session_id } = await getRelayUrl({\n businessKey: publicKey,\n authType,\n projectID: projectId,\n appId,\n });\n\n if (!relay_url) {\n throw new Error(\"Failed to get relay URL\");\n }\n\n sessionKey.current = cryptoService.generateSymmetricKey();\n sessionID.current = session_id;\n\n const buildUrl = `${relay_url}?sessionKey=${encodeURIComponent(\n sessionKey.current || \"\"\n )}&sessionID=${encodeURIComponent(\n sessionID.current || \"\"\n )}&authType=${encodeURIComponent(\n authType\n )}&projectId=${encodeURIComponent(\n projectId\n )}&publicKey=${encodeURIComponent(\n publicKey\n )}&callBackUrl=${encodeURIComponent(callBackUrl || \"\")}`;\n Linking.openURL(buildUrl);\n } catch (error) {\n console.log(error);\n }\n };\n\n const handleCallback = async () => {\n try {\n if (!sessionID.current) {\n return;\n }\n if (!sessionKey.current) {\n return;\n }\n const { cipher, nonce } = await getEncryptedData({\n businessKey: publicKey,\n authType,\n projectID: projectId,\n sessionID: sessionID.current,\n appId,\n });\n\n const decryptedData = cryptoService.decryptSymmetric({\n encrypted: { cipher, nonce },\n keyString: sessionKey.current,\n });\n\n if (decryptedData) {\n const result: IdentityResult = JSON.parse(decryptedData);\n\n onSuccess(result);\n sessionKey.current = null;\n sessionID.current = null;\n setLoading(false);\n onLoading?.(false);\n } else {\n onError?.(new Error(\"Failed to get identity result\"));\n sessionKey.current = null;\n sessionID.current = null;\n setLoading(false);\n onLoading?.(false);\n }\n } catch (error) {\n console.log(error);\n onError?.(error as Error);\n sessionKey.current = null;\n sessionID.current = null;\n setLoading(false);\n onLoading?.(false);\n }\n };\n\n React.useEffect(() => {\n const subscription = AppState.addEventListener(\"change\", (nextAppState) => {\n if (nextAppState === \"active\") {\n handleCallback();\n }\n });\n return () => {\n subscription.remove();\n };\n }, []);\n\n return (\n <View>\n <TouchableOpacity onPress={initialize} disabled={loading}>\n {buttonComponent || (\n <View\n style={{\n flexDirection: \"row\",\n alignItems: \"center\",\n gap: 10,\n paddingHorizontal: 10,\n paddingVertical: 10,\n borderRadius: 20,\n borderWidth: 1,\n }}\n >\n <Image\n source={{\n uri: \"https://res.cloudinary.com/de0jr8mcm/image/upload/v1765904735/pelican/pelican_icon_r9ghqw.png\",\n }}\n style={{ width: 30, height: 30 }}\n />\n <Text style={{ fontSize: 16, fontWeight: \"600\" }}>\n {authType === \"login\"\n ? \"Login with Pelican\"\n : authType === \"signup\"\n ? \"Signup with Pelican\"\n : authType === \"id-verification\"\n ? \"Verify identity with Pelican\"\n : \"Authenticate with Pelican\"}\n </Text>\n {loading && <ActivityIndicator color={\"#000\"} />}\n </View>\n )}\n </TouchableOpacity>\n </View>\n );\n};\n\nexport default PelicanAuth;\n"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { AppState, View, TouchableOpacity, Image, Text, ActivityIndicator, Linking } from 'react-native';
|
|
3
|
+
import 'react-native-get-random-values';
|
|
4
|
+
import { CryptoService, BASEURL } from '@pelican-identity/auth-core';
|
|
5
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
// src/components/PelicanAuth.tsx
|
|
8
|
+
var getRelayUrl = async ({
|
|
9
|
+
businessKey,
|
|
10
|
+
authType,
|
|
11
|
+
projectID,
|
|
12
|
+
appId
|
|
13
|
+
}) => {
|
|
14
|
+
if (!appId) {
|
|
15
|
+
throw new Error("App ID is required");
|
|
16
|
+
}
|
|
17
|
+
const headers = {
|
|
18
|
+
"Content-Type": "application/json",
|
|
19
|
+
"X-App-ID": appId
|
|
20
|
+
};
|
|
21
|
+
const response = await fetch(
|
|
22
|
+
`${BASEURL}/relay?public_key=${businessKey}&auth_type=${authType}&project_id=${projectID}`,
|
|
23
|
+
{ headers }
|
|
24
|
+
);
|
|
25
|
+
if (!response.ok) {
|
|
26
|
+
const error = await response.text();
|
|
27
|
+
throw new Error(`Failed to initiate authentication: ${error}`);
|
|
28
|
+
}
|
|
29
|
+
return response.json();
|
|
30
|
+
};
|
|
31
|
+
var getEncryptedData = async ({
|
|
32
|
+
businessKey,
|
|
33
|
+
authType,
|
|
34
|
+
projectID,
|
|
35
|
+
sessionID,
|
|
36
|
+
appId
|
|
37
|
+
}) => {
|
|
38
|
+
if (!appId) {
|
|
39
|
+
throw new Error("App ID is required");
|
|
40
|
+
}
|
|
41
|
+
const headers = {
|
|
42
|
+
"Content-Type": "application/json",
|
|
43
|
+
"X-App-ID": appId
|
|
44
|
+
};
|
|
45
|
+
const response = await fetch(
|
|
46
|
+
`${BASEURL}/get-rn-sdk-encrypted-data?public_key=${businessKey}&auth_type=${authType}&project_id=${projectID}&session_id=${sessionID}`,
|
|
47
|
+
{ headers }
|
|
48
|
+
);
|
|
49
|
+
if (!response.ok) {
|
|
50
|
+
const error = await response.text();
|
|
51
|
+
throw new Error(`Failed to get encrypted data: ${error}`);
|
|
52
|
+
}
|
|
53
|
+
return response.json();
|
|
54
|
+
};
|
|
55
|
+
var cryptoService = new CryptoService();
|
|
56
|
+
var PelicanAuth = ({
|
|
57
|
+
authType,
|
|
58
|
+
projectId,
|
|
59
|
+
publicKey,
|
|
60
|
+
onSuccess,
|
|
61
|
+
callBackUrl,
|
|
62
|
+
onError,
|
|
63
|
+
buttonComponent,
|
|
64
|
+
onLoading,
|
|
65
|
+
appId
|
|
66
|
+
}) => {
|
|
67
|
+
const sessionKey = React.useRef(null);
|
|
68
|
+
const sessionID = React.useRef(null);
|
|
69
|
+
const [loading, setLoading] = useState(false);
|
|
70
|
+
const initialize = async () => {
|
|
71
|
+
try {
|
|
72
|
+
setLoading(true);
|
|
73
|
+
onLoading?.(true);
|
|
74
|
+
const { relay_url, session_id } = await getRelayUrl({
|
|
75
|
+
businessKey: publicKey,
|
|
76
|
+
authType,
|
|
77
|
+
projectID: projectId,
|
|
78
|
+
appId
|
|
79
|
+
});
|
|
80
|
+
if (!relay_url) {
|
|
81
|
+
throw new Error("Failed to get relay URL");
|
|
82
|
+
}
|
|
83
|
+
sessionKey.current = cryptoService.generateSymmetricKey();
|
|
84
|
+
sessionID.current = session_id;
|
|
85
|
+
const buildUrl = `${relay_url}?sessionKey=${encodeURIComponent(
|
|
86
|
+
sessionKey.current || ""
|
|
87
|
+
)}&sessionID=${encodeURIComponent(
|
|
88
|
+
sessionID.current || ""
|
|
89
|
+
)}&authType=${encodeURIComponent(
|
|
90
|
+
authType
|
|
91
|
+
)}&projectId=${encodeURIComponent(
|
|
92
|
+
projectId
|
|
93
|
+
)}&publicKey=${encodeURIComponent(
|
|
94
|
+
publicKey
|
|
95
|
+
)}&callBackUrl=${encodeURIComponent(callBackUrl || "")}`;
|
|
96
|
+
Linking.openURL(buildUrl);
|
|
97
|
+
} catch (error) {
|
|
98
|
+
console.log(error);
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
const handleCallback = async () => {
|
|
102
|
+
try {
|
|
103
|
+
if (!sessionID.current) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (!sessionKey.current) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const { cipher, nonce } = await getEncryptedData({
|
|
110
|
+
businessKey: publicKey,
|
|
111
|
+
authType,
|
|
112
|
+
projectID: projectId,
|
|
113
|
+
sessionID: sessionID.current,
|
|
114
|
+
appId
|
|
115
|
+
});
|
|
116
|
+
const decryptedData = cryptoService.decryptSymmetric({
|
|
117
|
+
encrypted: { cipher, nonce },
|
|
118
|
+
keyString: sessionKey.current
|
|
119
|
+
});
|
|
120
|
+
if (decryptedData) {
|
|
121
|
+
const result = JSON.parse(decryptedData);
|
|
122
|
+
onSuccess(result);
|
|
123
|
+
sessionKey.current = null;
|
|
124
|
+
sessionID.current = null;
|
|
125
|
+
setLoading(false);
|
|
126
|
+
onLoading?.(false);
|
|
127
|
+
} else {
|
|
128
|
+
onError?.(new Error("Failed to get identity result"));
|
|
129
|
+
sessionKey.current = null;
|
|
130
|
+
sessionID.current = null;
|
|
131
|
+
setLoading(false);
|
|
132
|
+
onLoading?.(false);
|
|
133
|
+
}
|
|
134
|
+
} catch (error) {
|
|
135
|
+
console.log(error);
|
|
136
|
+
onError?.(error);
|
|
137
|
+
sessionKey.current = null;
|
|
138
|
+
sessionID.current = null;
|
|
139
|
+
setLoading(false);
|
|
140
|
+
onLoading?.(false);
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
React.useEffect(() => {
|
|
144
|
+
const subscription = AppState.addEventListener("change", (nextAppState) => {
|
|
145
|
+
if (nextAppState === "active") {
|
|
146
|
+
handleCallback();
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
return () => {
|
|
150
|
+
subscription.remove();
|
|
151
|
+
};
|
|
152
|
+
}, []);
|
|
153
|
+
return /* @__PURE__ */ jsx(View, { children: /* @__PURE__ */ jsx(TouchableOpacity, { onPress: initialize, disabled: loading, children: buttonComponent || /* @__PURE__ */ jsxs(
|
|
154
|
+
View,
|
|
155
|
+
{
|
|
156
|
+
style: {
|
|
157
|
+
flexDirection: "row",
|
|
158
|
+
alignItems: "center",
|
|
159
|
+
gap: 10,
|
|
160
|
+
paddingHorizontal: 10,
|
|
161
|
+
paddingVertical: 10,
|
|
162
|
+
borderRadius: 20,
|
|
163
|
+
borderWidth: 1
|
|
164
|
+
},
|
|
165
|
+
children: [
|
|
166
|
+
/* @__PURE__ */ jsx(
|
|
167
|
+
Image,
|
|
168
|
+
{
|
|
169
|
+
source: {
|
|
170
|
+
uri: "https://res.cloudinary.com/de0jr8mcm/image/upload/v1765904735/pelican/pelican_icon_r9ghqw.png"
|
|
171
|
+
},
|
|
172
|
+
style: { width: 30, height: 30 }
|
|
173
|
+
}
|
|
174
|
+
),
|
|
175
|
+
/* @__PURE__ */ jsx(Text, { style: { fontSize: 16, fontWeight: "600" }, children: authType === "login" ? "Login with Pelican" : authType === "signup" ? "Signup with Pelican" : authType === "id-verification" ? "Verify identity with Pelican" : "Authenticate with Pelican" }),
|
|
176
|
+
loading && /* @__PURE__ */ jsx(ActivityIndicator, { color: "#000" })
|
|
177
|
+
]
|
|
178
|
+
}
|
|
179
|
+
) }) });
|
|
180
|
+
};
|
|
181
|
+
var PelicanAuth_default = PelicanAuth;
|
|
182
|
+
|
|
183
|
+
export { PelicanAuth_default as PelicanAuth };
|
|
184
|
+
//# sourceMappingURL=index.mjs.map
|
|
185
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utilities.ts","../src/components/PelicanAuth.tsx"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import \"react-native-get-random-values\";\n\nimport { BASEURL, type AuthType } from \"@pelican-identity/auth-core\";\n\nexport const getRelayUrl = async ({\n businessKey,\n authType,\n projectID,\n appId,\n}: {\n businessKey: string;\n authType: AuthType;\n projectID: string;\n appId: string;\n}) => {\n if (!appId) {\n throw new Error(\"App ID is required\");\n }\n const headers = {\n \"Content-Type\": \"application/json\",\n \"X-App-ID\": appId,\n };\n\n const response = await fetch(\n `${BASEURL}/relay?public_key=${businessKey}&auth_type=${authType}&project_id=${projectID}`,\n { headers }\n );\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`Failed to initiate authentication: ${error}`);\n }\n\n return response.json() as Promise<{ relay_url: string; session_id: string }>;\n};\n\nexport const getEncryptedData = async ({\n businessKey,\n authType,\n projectID,\n sessionID,\n appId,\n}: {\n businessKey: string;\n authType: AuthType;\n projectID: string;\n sessionID: string;\n appId: string;\n}) => {\n if (!appId) {\n throw new Error(\"App ID is required\");\n }\n const headers = {\n \"Content-Type\": \"application/json\",\n \"X-App-ID\": appId,\n };\n\n const response = await fetch(\n `${BASEURL}/get-rn-sdk-encrypted-data?public_key=${businessKey}&auth_type=${authType}&project_id=${projectID}&session_id=${sessionID}`,\n { headers }\n );\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`Failed to get encrypted data: ${error}`);\n }\n\n return response.json() as Promise<{ cipher: string; nonce: string }>;\n};\n","import React, { useState } from \"react\";\nimport {\n ActivityIndicator,\n AppState,\n Image,\n Linking,\n Text,\n TouchableOpacity,\n View,\n} from \"react-native\";\nimport \"react-native-get-random-values\";\n\nimport { getEncryptedData, getRelayUrl } from \"../utilities\";\nimport { PelicanRNAuthProps } from \"../types\";\nimport { IdentityResult, CryptoService } from \"@pelican-identity/auth-core\";\n\nconst cryptoService = new CryptoService();\nconst PelicanAuth = ({\n authType,\n projectId,\n publicKey,\n onSuccess,\n callBackUrl,\n onError,\n buttonComponent,\n onLoading,\n appId,\n}: PelicanRNAuthProps) => {\n const sessionKey = React.useRef<string | null>(null);\n const sessionID = React.useRef<string | null>(null);\n const [loading, setLoading] = useState(false);\n\n const initialize = async () => {\n try {\n setLoading(true);\n onLoading?.(true);\n const { relay_url, session_id } = await getRelayUrl({\n businessKey: publicKey,\n authType,\n projectID: projectId,\n appId,\n });\n\n if (!relay_url) {\n throw new Error(\"Failed to get relay URL\");\n }\n\n sessionKey.current = cryptoService.generateSymmetricKey();\n sessionID.current = session_id;\n\n const buildUrl = `${relay_url}?sessionKey=${encodeURIComponent(\n sessionKey.current || \"\"\n )}&sessionID=${encodeURIComponent(\n sessionID.current || \"\"\n )}&authType=${encodeURIComponent(\n authType\n )}&projectId=${encodeURIComponent(\n projectId\n )}&publicKey=${encodeURIComponent(\n publicKey\n )}&callBackUrl=${encodeURIComponent(callBackUrl || \"\")}`;\n Linking.openURL(buildUrl);\n } catch (error) {\n console.log(error);\n }\n };\n\n const handleCallback = async () => {\n try {\n if (!sessionID.current) {\n return;\n }\n if (!sessionKey.current) {\n return;\n }\n const { cipher, nonce } = await getEncryptedData({\n businessKey: publicKey,\n authType,\n projectID: projectId,\n sessionID: sessionID.current,\n appId,\n });\n\n const decryptedData = cryptoService.decryptSymmetric({\n encrypted: { cipher, nonce },\n keyString: sessionKey.current,\n });\n\n if (decryptedData) {\n const result: IdentityResult = JSON.parse(decryptedData);\n\n onSuccess(result);\n sessionKey.current = null;\n sessionID.current = null;\n setLoading(false);\n onLoading?.(false);\n } else {\n onError?.(new Error(\"Failed to get identity result\"));\n sessionKey.current = null;\n sessionID.current = null;\n setLoading(false);\n onLoading?.(false);\n }\n } catch (error) {\n console.log(error);\n onError?.(error as Error);\n sessionKey.current = null;\n sessionID.current = null;\n setLoading(false);\n onLoading?.(false);\n }\n };\n\n React.useEffect(() => {\n const subscription = AppState.addEventListener(\"change\", (nextAppState) => {\n if (nextAppState === \"active\") {\n handleCallback();\n }\n });\n return () => {\n subscription.remove();\n };\n }, []);\n\n return (\n <View>\n <TouchableOpacity onPress={initialize} disabled={loading}>\n {buttonComponent || (\n <View\n style={{\n flexDirection: \"row\",\n alignItems: \"center\",\n gap: 10,\n paddingHorizontal: 10,\n paddingVertical: 10,\n borderRadius: 20,\n borderWidth: 1,\n }}\n >\n <Image\n source={{\n uri: \"https://res.cloudinary.com/de0jr8mcm/image/upload/v1765904735/pelican/pelican_icon_r9ghqw.png\",\n }}\n style={{ width: 30, height: 30 }}\n />\n <Text style={{ fontSize: 16, fontWeight: \"600\" }}>\n {authType === \"login\"\n ? \"Login with Pelican\"\n : authType === \"signup\"\n ? \"Signup with Pelican\"\n : authType === \"id-verification\"\n ? \"Verify identity with Pelican\"\n : \"Authenticate with Pelican\"}\n </Text>\n {loading && <ActivityIndicator color={\"#000\"} />}\n </View>\n )}\n </TouchableOpacity>\n </View>\n );\n};\n\nexport default PelicanAuth;\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pelican-identity/react-native",
|
|
3
|
+
"version": "1.2.0",
|
|
4
|
+
"description": "React Native components for Pelican Identity authentication",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"react-native": "./src/index.ts",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.mjs",
|
|
11
|
+
"require": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"README.md",
|
|
17
|
+
"package.json"
|
|
18
|
+
],
|
|
19
|
+
"peerDependencies": {
|
|
20
|
+
"react": "^17.0.0 || ^18.0.0",
|
|
21
|
+
"react-native": ">=0.60.0",
|
|
22
|
+
"react-native-get-random-values": "^1.7.0"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@pelican-identity/auth-core": "1.2.0"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/react": "^18.2.45",
|
|
29
|
+
"@types/react-native": "^0.72.0",
|
|
30
|
+
"react": "^18.2.0",
|
|
31
|
+
"react-native": "^0.72.0",
|
|
32
|
+
"tsup": "^8.0.1",
|
|
33
|
+
"typescript": "^5.3.3"
|
|
34
|
+
},
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "https://github.com/Pelican-Identity/pelican-sdk.git",
|
|
38
|
+
"directory": "packages/react-native"
|
|
39
|
+
},
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "public"
|
|
42
|
+
},
|
|
43
|
+
"scripts": {
|
|
44
|
+
"build": "tsup",
|
|
45
|
+
"dev": "tsup --watch",
|
|
46
|
+
"typecheck": "tsc --noEmit"
|
|
47
|
+
}
|
|
48
|
+
}
|