@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 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
@@ -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 };
@@ -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
+ }