@getpara/react-native-wallet 2.0.0-dev.0 → 2.0.0-dev.2
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/dist/AsyncStorage.d.ts +2 -5
- package/dist/AsyncStorage.js +37 -14
- package/dist/KeychainStorage.d.ts +0 -3
- package/dist/KeychainStorage.js +35 -28
- package/dist/config.d.ts +3 -0
- package/dist/config.js +1 -0
- package/dist/react-native/ParaMobile.d.ts +6 -22
- package/dist/react-native/ParaMobile.js +36 -77
- package/dist/react-native/ReactNativeUtils.d.ts +3 -2
- package/dist/react-native/ReactNativeUtils.js +8 -7
- package/dist/shim.js +7 -0
- package/package.json +62 -29
- package/src/AsyncStorage.ts +32 -14
- package/src/KeychainStorage.ts +34 -31
- package/src/config.ts +2 -0
- package/src/react-native/ParaMobile.ts +41 -72
- package/src/react-native/ReactNativeUtils.ts +9 -8
- package/src/shim.js +8 -0
- /package/src/{index.tsx → index.ts} +0 -0
package/dist/AsyncStorage.d.ts
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import { StorageUtils } from '@getpara/web-sdk';
|
|
2
|
-
/**
|
|
3
|
-
* Implements `StorageUtils` using React Native Async Storage.
|
|
4
|
-
*/
|
|
5
2
|
export declare class AsyncStorage implements StorageUtils {
|
|
6
|
-
clear(prefix: string): Promise<void>;
|
|
7
3
|
get(key: string): Promise<string | null>;
|
|
8
|
-
removeItem(key: string): Promise<void>;
|
|
9
4
|
set(key: string, value: string): Promise<void>;
|
|
5
|
+
removeItem(key: string): Promise<void>;
|
|
6
|
+
clear(prefix: string): Promise<void>;
|
|
10
7
|
}
|
package/dist/AsyncStorage.js
CHANGED
|
@@ -8,33 +8,56 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
10
|
import RNAsyncStorage from '@react-native-async-storage/async-storage';
|
|
11
|
-
/**
|
|
12
|
-
* Implements `StorageUtils` using React Native Async Storage.
|
|
13
|
-
*/
|
|
14
11
|
export class AsyncStorage {
|
|
15
|
-
|
|
12
|
+
get(key) {
|
|
16
13
|
return __awaiter(this, void 0, void 0, function* () {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
14
|
+
try {
|
|
15
|
+
return yield RNAsyncStorage.getItem(key);
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
console.warn('Error retrieving stored item:', error);
|
|
19
|
+
return null;
|
|
22
20
|
}
|
|
23
21
|
});
|
|
24
22
|
}
|
|
25
|
-
|
|
23
|
+
set(key, value) {
|
|
26
24
|
return __awaiter(this, void 0, void 0, function* () {
|
|
27
|
-
|
|
25
|
+
try {
|
|
26
|
+
yield RNAsyncStorage.setItem(key, value);
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
console.warn(`Error storing key ${key}:`, error);
|
|
30
|
+
}
|
|
28
31
|
});
|
|
29
32
|
}
|
|
30
33
|
removeItem(key) {
|
|
31
34
|
return __awaiter(this, void 0, void 0, function* () {
|
|
32
|
-
|
|
35
|
+
try {
|
|
36
|
+
yield RNAsyncStorage.removeItem(key);
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
console.warn(`Error removing key ${key}:`, error);
|
|
40
|
+
}
|
|
33
41
|
});
|
|
34
42
|
}
|
|
35
|
-
|
|
43
|
+
clear(prefix) {
|
|
36
44
|
return __awaiter(this, void 0, void 0, function* () {
|
|
37
|
-
|
|
45
|
+
try {
|
|
46
|
+
const keys = yield RNAsyncStorage.getAllKeys();
|
|
47
|
+
for (const key of keys) {
|
|
48
|
+
if (key.startsWith(prefix)) {
|
|
49
|
+
try {
|
|
50
|
+
yield RNAsyncStorage.removeItem(key);
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
console.warn(`Error clearing key ${key}:`, error);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
console.warn(`Error getting keys for prefix ${prefix}:`, error);
|
|
60
|
+
}
|
|
38
61
|
});
|
|
39
62
|
}
|
|
40
63
|
}
|
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
import { StorageUtils } from '@getpara/web-sdk';
|
|
2
|
-
/**
|
|
3
|
-
* Implements `StorageUtils` using React Native `Keychain`.
|
|
4
|
-
*/
|
|
5
2
|
export declare class KeychainStorage implements StorageUtils {
|
|
6
3
|
get(key: string): Promise<string | null>;
|
|
7
4
|
set(key: string, value: string): Promise<void>;
|
package/dist/KeychainStorage.js
CHANGED
|
@@ -9,17 +9,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
};
|
|
10
10
|
import Keychain from 'react-native-keychain';
|
|
11
11
|
const USERNAME = '@CAPSULE';
|
|
12
|
-
const KEYCHAIN_USER_CANCELLED_ERRORS = [
|
|
13
|
-
'user canceled the operation',
|
|
14
|
-
'error: code: 13, msg: cancel',
|
|
15
|
-
'error: code: 10, msg: fingerprint operation canceled by the user',
|
|
16
|
-
];
|
|
17
|
-
function isUserCancelledError(error) {
|
|
18
|
-
return KEYCHAIN_USER_CANCELLED_ERRORS.some(userCancelledError => error.toString().toLowerCase().includes(userCancelledError));
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Implements `StorageUtils` using React Native `Keychain`.
|
|
22
|
-
*/
|
|
23
12
|
export class KeychainStorage {
|
|
24
13
|
get(key) {
|
|
25
14
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -33,39 +22,57 @@ export class KeychainStorage {
|
|
|
33
22
|
return item.password;
|
|
34
23
|
}
|
|
35
24
|
catch (error) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
throw new Error('Error retrieving stored item ' + error.message);
|
|
39
|
-
}
|
|
40
|
-
throw error;
|
|
25
|
+
console.warn('Error retrieving stored item:', error);
|
|
26
|
+
return null;
|
|
41
27
|
}
|
|
42
28
|
});
|
|
43
29
|
}
|
|
44
30
|
set(key, value) {
|
|
45
31
|
return __awaiter(this, void 0, void 0, function* () {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
32
|
+
try {
|
|
33
|
+
const result = yield Keychain.setGenericPassword(USERNAME, value, {
|
|
34
|
+
service: key,
|
|
35
|
+
accessible: Keychain.ACCESSIBLE.WHEN_UNLOCKED,
|
|
36
|
+
securityLevel: Keychain.SECURITY_LEVEL.ANY,
|
|
37
|
+
storage: Keychain.STORAGE_TYPE.AES_GCM_NO_AUTH,
|
|
38
|
+
});
|
|
39
|
+
if (!result) {
|
|
40
|
+
console.warn(`Failed to store key ${key}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
console.warn(`Error storing key ${key}:`, error);
|
|
53
45
|
}
|
|
54
46
|
});
|
|
55
47
|
}
|
|
56
48
|
removeItem(key) {
|
|
57
49
|
return __awaiter(this, void 0, void 0, function* () {
|
|
58
|
-
|
|
50
|
+
try {
|
|
51
|
+
yield Keychain.resetGenericPassword({ service: key });
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
console.warn(`Error removing key ${key}:`, error);
|
|
55
|
+
}
|
|
59
56
|
});
|
|
60
57
|
}
|
|
61
58
|
clear(prefix) {
|
|
62
59
|
return __awaiter(this, void 0, void 0, function* () {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
60
|
+
try {
|
|
61
|
+
const services = yield Keychain.getAllGenericPasswordServices();
|
|
62
|
+
for (const key of services) {
|
|
63
|
+
if (key && key.startsWith(prefix)) {
|
|
64
|
+
try {
|
|
65
|
+
yield Keychain.resetGenericPassword({ service: key });
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
console.warn(`Error clearing key ${key}:`, error);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
67
71
|
}
|
|
68
72
|
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
console.warn(`Error getting services for prefix ${prefix}:`, error);
|
|
75
|
+
}
|
|
69
76
|
});
|
|
70
77
|
}
|
|
71
78
|
}
|
package/dist/config.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { Environment } from '@getpara/web-sdk';
|
|
2
|
+
declare function getPortalBaseURL(env: Environment): "http://localhost:3003" | "https://app.sandbox.usecapsule.com" | "https://app.beta.usecapsule.com" | "https://app.usecapsule.com";
|
|
3
|
+
declare function getBaseUrl(env: Environment): string;
|
|
2
4
|
export declare function getBaseMPCNetworkWSUrl(env: Environment): string;
|
|
3
5
|
export declare let userManagementServer: string;
|
|
4
6
|
export declare let portalBase: string;
|
|
5
7
|
export declare let mpcNetworkWSServer: string;
|
|
6
8
|
export declare function setEnv(env: Environment): void;
|
|
7
9
|
export declare const DEBUG_MODE_ENABLED = false;
|
|
10
|
+
export { getBaseUrl, getPortalBaseURL };
|
package/dist/config.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { ConstructorOpts, ParaCore, Environment, PlatformUtils } from '@getpara/web-sdk';
|
|
2
|
-
import { Auth } from '@getpara/user-management-client';
|
|
1
|
+
import { AuthStateSignup, ConstructorOpts, ParaCore, Environment, PlatformUtils } from '@getpara/web-sdk';
|
|
3
2
|
/**
|
|
4
3
|
* Represents a mobile implementation of the Para SDK.
|
|
5
4
|
* @extends ParaCore
|
|
@@ -8,6 +7,7 @@ import { Auth } from '@getpara/user-management-client';
|
|
|
8
7
|
* const para = new ParaMobile(Environment.BETA, "api_key");
|
|
9
8
|
*/
|
|
10
9
|
export declare class ParaMobile extends ParaCore {
|
|
10
|
+
isNativePasskey: boolean;
|
|
11
11
|
private relyingPartyId;
|
|
12
12
|
/**
|
|
13
13
|
* Creates an instance of ParaMobile.
|
|
@@ -17,36 +17,20 @@ export declare class ParaMobile extends ParaCore {
|
|
|
17
17
|
* @param {ConstructorOpts} [opts] - Additional constructor options.
|
|
18
18
|
*/
|
|
19
19
|
constructor(env: Environment, apiKey: string, relyingPartyId?: string, opts?: ConstructorOpts);
|
|
20
|
+
protected ready(): Promise<void>;
|
|
20
21
|
protected getPlatformUtils(): PlatformUtils;
|
|
21
|
-
|
|
22
|
-
* Verifies an email and returns the biometrics ID.
|
|
23
|
-
* @param {string} verificationCode - The verification code sent to the email.
|
|
24
|
-
* @returns {Promise<string>} The biometrics ID.
|
|
25
|
-
*/
|
|
26
|
-
verifyEmailBiometricsId({ verificationCode }: {
|
|
27
|
-
verificationCode: string;
|
|
28
|
-
}): Promise<string>;
|
|
29
|
-
/**
|
|
30
|
-
* Verifies a phone number and returns the biometrics ID.
|
|
31
|
-
* @param {string} verificationCode - The verification code sent to the phone.
|
|
32
|
-
* @returns {Promise<string>} The biometrics ID.
|
|
33
|
-
*/
|
|
34
|
-
verifyPhoneBiometricsId({ verificationCode }: {
|
|
35
|
-
verificationCode: string;
|
|
36
|
-
}): Promise<string>;
|
|
22
|
+
isPasskeySupported(): Promise<boolean>;
|
|
37
23
|
/**
|
|
38
24
|
* Registers a passkey for the user.
|
|
39
25
|
* @param {Auth<'email'> | Auth<'phone'>} auth - The user's authentication details
|
|
40
26
|
* @param {string} biometricsId - The biometrics ID obtained from verification.
|
|
41
27
|
* @returns {Promise<void>}
|
|
42
28
|
*/
|
|
43
|
-
registerPasskey(
|
|
44
|
-
biometricsId: string;
|
|
45
|
-
} & (Auth<'email'> | Auth<'phone'>)): Promise<void>;
|
|
29
|
+
registerPasskey(authState: AuthStateSignup): Promise<void>;
|
|
46
30
|
/**
|
|
47
31
|
* Logs in the user using their authentication credentials.
|
|
48
32
|
* @param {AuthParams} params - The authentication parameters.
|
|
49
33
|
* @returns {Promise<void>}
|
|
50
34
|
*/
|
|
51
|
-
|
|
35
|
+
loginWithPasskey(): Promise<void>;
|
|
52
36
|
}
|
|
@@ -7,22 +7,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
-
var __rest = (this && this.__rest) || function (s, e) {
|
|
11
|
-
var t = {};
|
|
12
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
13
|
-
t[p] = s[p];
|
|
14
|
-
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
15
|
-
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
16
|
-
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
17
|
-
t[p[i]] = s[p[i]];
|
|
18
|
-
}
|
|
19
|
-
return t;
|
|
20
|
-
};
|
|
21
10
|
import { ParaCore, Environment, decryptPrivateKeyAndDecryptShare, encryptPrivateKey, getAsymmetricKeyPair, getDerivedPrivateKeyAndDecrypt, getPublicKeyHex, getSHA256HashHex, parseCredentialCreationRes, } from '@getpara/web-sdk';
|
|
22
|
-
import * as Sentry from '@sentry/react-native';
|
|
23
11
|
import { ReactNativeUtils } from './ReactNativeUtils.js';
|
|
24
12
|
import { Passkey, } from 'react-native-passkey';
|
|
25
|
-
import {
|
|
13
|
+
import { PublicKeyStatus } from '@getpara/user-management-client';
|
|
26
14
|
import { setEnv } from '../config.js';
|
|
27
15
|
import base64url from 'base64url';
|
|
28
16
|
import { webcrypto } from 'crypto';
|
|
@@ -45,14 +33,7 @@ export class ParaMobile extends ParaCore {
|
|
|
45
33
|
*/
|
|
46
34
|
constructor(env, apiKey, relyingPartyId, opts) {
|
|
47
35
|
super(env, apiKey, opts);
|
|
48
|
-
|
|
49
|
-
// will turn on in prod after monitoring
|
|
50
|
-
if (env !== Environment.PROD && env !== Environment.DEV) {
|
|
51
|
-
Sentry.init({
|
|
52
|
-
environment: env.toLowerCase(),
|
|
53
|
-
dsn: 'https://59cea0cfbbb30a646c4e9f2feea06da4@o4504568036720640.ingest.us.sentry.io/4508850922323968',
|
|
54
|
-
});
|
|
55
|
-
}
|
|
36
|
+
this.isNativePasskey = true;
|
|
56
37
|
setEnv(env);
|
|
57
38
|
if (relyingPartyId) {
|
|
58
39
|
this.relyingPartyId = relyingPartyId;
|
|
@@ -73,41 +54,17 @@ export class ParaMobile extends ParaCore {
|
|
|
73
54
|
}
|
|
74
55
|
}
|
|
75
56
|
}
|
|
57
|
+
ready() {
|
|
58
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
59
|
+
this.isReady = true;
|
|
60
|
+
});
|
|
61
|
+
}
|
|
76
62
|
getPlatformUtils() {
|
|
77
63
|
return new ReactNativeUtils();
|
|
78
64
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
* @returns {Promise<string>} The biometrics ID.
|
|
83
|
-
*/
|
|
84
|
-
verifyEmailBiometricsId(_a) {
|
|
85
|
-
const _super = Object.create(null, {
|
|
86
|
-
verifyEmail: { get: () => super.verifyEmail }
|
|
87
|
-
});
|
|
88
|
-
return __awaiter(this, arguments, void 0, function* ({ verificationCode }) {
|
|
89
|
-
const webAuthCreateUrl = yield _super.verifyEmail.call(this, { verificationCode });
|
|
90
|
-
const segments = webAuthCreateUrl.split('/');
|
|
91
|
-
const segments2 = segments[segments.length - 1].split('?');
|
|
92
|
-
const biometricsId = segments2[0];
|
|
93
|
-
return biometricsId;
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* Verifies a phone number and returns the biometrics ID.
|
|
98
|
-
* @param {string} verificationCode - The verification code sent to the phone.
|
|
99
|
-
* @returns {Promise<string>} The biometrics ID.
|
|
100
|
-
*/
|
|
101
|
-
verifyPhoneBiometricsId(_a) {
|
|
102
|
-
const _super = Object.create(null, {
|
|
103
|
-
verifyPhone: { get: () => super.verifyPhone }
|
|
104
|
-
});
|
|
105
|
-
return __awaiter(this, arguments, void 0, function* ({ verificationCode }) {
|
|
106
|
-
const webAuthCreateUrl = yield _super.verifyPhone.call(this, { verificationCode });
|
|
107
|
-
const segments = webAuthCreateUrl.split('/');
|
|
108
|
-
const segments2 = segments[segments.length - 1].split('?');
|
|
109
|
-
const biometricsId = segments2[0];
|
|
110
|
-
return biometricsId;
|
|
65
|
+
isPasskeySupported() {
|
|
66
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
67
|
+
return Passkey.isSupported();
|
|
111
68
|
});
|
|
112
69
|
}
|
|
113
70
|
/**
|
|
@@ -116,16 +73,19 @@ export class ParaMobile extends ParaCore {
|
|
|
116
73
|
* @param {string} biometricsId - The biometrics ID obtained from verification.
|
|
117
74
|
* @returns {Promise<void>}
|
|
118
75
|
*/
|
|
119
|
-
registerPasskey(
|
|
76
|
+
registerPasskey(authState) {
|
|
120
77
|
return __awaiter(this, void 0, void 0, function* () {
|
|
121
|
-
|
|
78
|
+
if (!authState.passkeyId) {
|
|
79
|
+
throw new Error('Passkey ID not found. Make sure you have enabled passkey logins in the Para Developer Portal.');
|
|
80
|
+
}
|
|
81
|
+
const userId = this.assertUserId();
|
|
82
|
+
const authInfo = this.assertIsAuthSet();
|
|
122
83
|
if (!webcrypto || !webcrypto.getRandomValues) {
|
|
123
84
|
throw new Error('Web crypto is not available. Ensure you have imported the shim from @getpara/react-native-wallet.');
|
|
124
85
|
}
|
|
125
86
|
const userHandle = new Uint8Array(32);
|
|
126
87
|
webcrypto.getRandomValues(userHandle);
|
|
127
88
|
const userHandleEncoded = base64url.encode(userHandle);
|
|
128
|
-
const { identifier: displayIdentifier } = extractAuthInfo(auth, { isRequired: true });
|
|
129
89
|
const requestJson = {
|
|
130
90
|
authenticatorSelection: {
|
|
131
91
|
authenticatorAttachment: 'platform',
|
|
@@ -139,8 +99,8 @@ export class ParaMobile extends ParaCore {
|
|
|
139
99
|
},
|
|
140
100
|
user: {
|
|
141
101
|
id: userHandleEncoded,
|
|
142
|
-
name:
|
|
143
|
-
displayName:
|
|
102
|
+
name: authInfo.identifier,
|
|
103
|
+
displayName: authInfo.identifier,
|
|
144
104
|
},
|
|
145
105
|
pubKeyCredParams: [
|
|
146
106
|
{
|
|
@@ -170,14 +130,14 @@ export class ParaMobile extends ParaCore {
|
|
|
170
130
|
const encryptionKeyHash = getSHA256HashHex(userHandleEncoded);
|
|
171
131
|
const encryptedPrivateKeyHex = yield encryptPrivateKey(keyPair, userHandleEncoded);
|
|
172
132
|
const { partnerId } = yield this.ctx.client.touchSession();
|
|
173
|
-
yield this.ctx.client.patchSessionPublicKey(partnerId,
|
|
133
|
+
yield this.ctx.client.patchSessionPublicKey(partnerId, userId, authState.passkeyId, {
|
|
174
134
|
publicKey: resultJson.id,
|
|
175
135
|
sigDerivedPublicKey: publicKeyHex,
|
|
176
136
|
cosePublicKey,
|
|
177
137
|
clientDataJSON,
|
|
178
138
|
status: PublicKeyStatus.COMPLETE,
|
|
179
139
|
});
|
|
180
|
-
yield this.ctx.client.uploadEncryptedWalletPrivateKey(
|
|
140
|
+
yield this.ctx.client.uploadEncryptedWalletPrivateKey(userId, encryptedPrivateKeyHex, encryptionKeyHash, resultJson.id);
|
|
181
141
|
});
|
|
182
142
|
}
|
|
183
143
|
/**
|
|
@@ -185,12 +145,11 @@ export class ParaMobile extends ParaCore {
|
|
|
185
145
|
* @param {AuthParams} params - The authentication parameters.
|
|
186
146
|
* @returns {Promise<void>}
|
|
187
147
|
*/
|
|
188
|
-
|
|
148
|
+
loginWithPasskey() {
|
|
189
149
|
return __awaiter(this, void 0, void 0, function* () {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
const
|
|
193
|
-
const { challenge, allowedPublicKeys } = yield this.ctx.client.getWebChallenge(authInfo.auth);
|
|
150
|
+
this.assertIsAuthSet();
|
|
151
|
+
const userId = this.assertUserId();
|
|
152
|
+
const { challenge, allowedPublicKeys } = yield this.ctx.client.getWebChallenge({ userId });
|
|
194
153
|
const requestJson = {
|
|
195
154
|
challenge,
|
|
196
155
|
timeout: 60000,
|
|
@@ -205,7 +164,7 @@ export class ParaMobile extends ParaCore {
|
|
|
205
164
|
else {
|
|
206
165
|
resultJson = result;
|
|
207
166
|
}
|
|
208
|
-
const { partnerId } = yield this.ctx.client.touchSession();
|
|
167
|
+
const { partnerId, sessionLookupId } = yield this.ctx.client.touchSession();
|
|
209
168
|
const publicKey = resultJson.id;
|
|
210
169
|
const verifyWebChallengeResult = yield this.ctx.client.verifyWebChallenge(partnerId, {
|
|
211
170
|
publicKey,
|
|
@@ -215,17 +174,8 @@ export class ParaMobile extends ParaCore {
|
|
|
215
174
|
signature: resultJson.response.signature,
|
|
216
175
|
},
|
|
217
176
|
});
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
const { user } = yield this.ctx.client.getUser(userId);
|
|
221
|
-
if (user.phone) {
|
|
222
|
-
yield this.setPhoneNumber(user.phone.number, user.phone.countryCode);
|
|
223
|
-
}
|
|
224
|
-
if (user.email) {
|
|
225
|
-
yield this.setEmail(user.email);
|
|
226
|
-
}
|
|
227
|
-
if (user.farcasterUsername) {
|
|
228
|
-
yield this.setFarcasterUsername(user.farcasterUsername);
|
|
177
|
+
if (userId !== verifyWebChallengeResult.data.userId) {
|
|
178
|
+
throw new Error('User ID mismatch');
|
|
229
179
|
}
|
|
230
180
|
const encryptedSharesResult = yield this.ctx.client.getBiometricKeyshares(userId, resultJson.id);
|
|
231
181
|
const encryptionKeyHash = getSHA256HashHex(resultJson.response.userHandle);
|
|
@@ -254,7 +204,16 @@ export class ParaMobile extends ParaCore {
|
|
|
254
204
|
type: desiredWallet.type || undefined,
|
|
255
205
|
};
|
|
256
206
|
}
|
|
207
|
+
const currentWalletIds = {};
|
|
208
|
+
for (const wallet of Object.values(walletsToInsert)) {
|
|
209
|
+
const { id, type } = wallet;
|
|
210
|
+
const currentIdsForType = currentWalletIds[type || 'EVM'] || [];
|
|
211
|
+
currentWalletIds[type || 'EVM'] = [...currentIdsForType, id];
|
|
212
|
+
}
|
|
257
213
|
yield this.setWallets(walletsToInsert);
|
|
214
|
+
yield this.setCurrentWalletIds(currentWalletIds, {
|
|
215
|
+
sessionLookupId,
|
|
216
|
+
});
|
|
258
217
|
});
|
|
259
218
|
}
|
|
260
219
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { PlatformUtils, TPregenIdentifierType } from '@getpara/web-sdk';
|
|
2
2
|
import { Ctx } from '@getpara/web-sdk';
|
|
3
3
|
import { SignatureRes } from '@getpara/web-sdk';
|
|
4
|
-
import { BackupKitEmailProps,
|
|
4
|
+
import { BackupKitEmailProps, TWalletType, SDKType } from '@getpara/user-management-client';
|
|
5
5
|
import { AsyncStorage } from '../AsyncStorage.js';
|
|
6
6
|
import { KeychainStorage } from '../KeychainStorage.js';
|
|
7
7
|
export declare class ReactNativeUtils implements PlatformUtils {
|
|
8
|
+
sdkType: SDKType;
|
|
8
9
|
disableProviderModal?: boolean | undefined;
|
|
9
10
|
localStorage: AsyncStorage;
|
|
10
11
|
sessionStorage: AsyncStorage;
|
|
@@ -14,7 +15,7 @@ export declare class ReactNativeUtils implements PlatformUtils {
|
|
|
14
15
|
p: string;
|
|
15
16
|
q: string;
|
|
16
17
|
}>;
|
|
17
|
-
keygen(ctx: Ctx, userId: string, type: Exclude<
|
|
18
|
+
keygen(ctx: Ctx, userId: string, type: Exclude<TWalletType, 'SOLANA'>, _secretKey: string | null, _sessionCookie: string, _emailProps?: BackupKitEmailProps | undefined): Promise<{
|
|
18
19
|
signer: string;
|
|
19
20
|
walletId: string;
|
|
20
21
|
}>;
|
|
@@ -7,7 +7,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
-
import { KeyShareType
|
|
10
|
+
import { KeyShareType } from '@getpara/user-management-client';
|
|
11
11
|
import { NativeModules } from 'react-native';
|
|
12
12
|
import { AsyncStorage } from '../AsyncStorage.js';
|
|
13
13
|
import { KeychainStorage } from '../KeychainStorage.js';
|
|
@@ -47,6 +47,7 @@ function sendTransactionRequest(ctx, userId, walletId, protocolId, transaction,
|
|
|
47
47
|
}
|
|
48
48
|
export class ReactNativeUtils {
|
|
49
49
|
constructor() {
|
|
50
|
+
this.sdkType = 'REACT_NATIVE';
|
|
50
51
|
this.localStorage = new AsyncStorage();
|
|
51
52
|
this.sessionStorage = new AsyncStorage();
|
|
52
53
|
this.secureStorage = new KeychainStorage();
|
|
@@ -63,7 +64,7 @@ export class ReactNativeUtils {
|
|
|
63
64
|
const { walletId, protocolId } = yield ctx.client.createWallet(userId, {
|
|
64
65
|
type,
|
|
65
66
|
useTwoSigners: true,
|
|
66
|
-
scheme: ctx.useDKLS ?
|
|
67
|
+
scheme: ctx.useDKLS ? 'DKLS' : 'CGGMP',
|
|
67
68
|
});
|
|
68
69
|
if (ctx.mpcComputationClient && !ctx.useDKLS) {
|
|
69
70
|
const { signer } = yield keygenRequest(ctx, userId, walletId, protocolId);
|
|
@@ -141,8 +142,8 @@ export class ReactNativeUtils {
|
|
|
141
142
|
ed25519Keygen(ctx, userId, _sessionCookie, _emailProps) {
|
|
142
143
|
return __awaiter(this, void 0, void 0, function* () {
|
|
143
144
|
const { walletId, protocolId } = yield ctx.client.createWallet(userId, {
|
|
144
|
-
scheme:
|
|
145
|
-
type:
|
|
145
|
+
scheme: 'ED25519',
|
|
146
|
+
type: 'SOLANA',
|
|
146
147
|
});
|
|
147
148
|
const signer = yield ParaSignerModule.ed25519CreateAccount(walletId, protocolId);
|
|
148
149
|
return { signer, walletId };
|
|
@@ -153,8 +154,8 @@ export class ReactNativeUtils {
|
|
|
153
154
|
const { walletId, protocolId } = yield ctx.client.createPregenWallet({
|
|
154
155
|
pregenIdentifier,
|
|
155
156
|
pregenIdentifierType,
|
|
156
|
-
scheme:
|
|
157
|
-
type:
|
|
157
|
+
scheme: 'ED25519',
|
|
158
|
+
type: 'SOLANA',
|
|
158
159
|
});
|
|
159
160
|
const signer = yield ParaSignerModule.ed25519CreateAccount(walletId, protocolId);
|
|
160
161
|
return { signer, walletId };
|
|
@@ -162,7 +163,7 @@ export class ReactNativeUtils {
|
|
|
162
163
|
}
|
|
163
164
|
ed25519Sign(ctx, userId, walletId, share, base64Bytes, _sessionCookie) {
|
|
164
165
|
return __awaiter(this, void 0, void 0, function* () {
|
|
165
|
-
const { protocolId } = yield ctx.client.preSignMessage(userId, walletId, base64Bytes,
|
|
166
|
+
const { protocolId } = yield ctx.client.preSignMessage(userId, walletId, base64Bytes, 'ED25519');
|
|
166
167
|
const base64Sig = yield ParaSignerModule.ed25519Sign(protocolId, share, base64Bytes);
|
|
167
168
|
return { signature: base64Sig };
|
|
168
169
|
});
|
package/dist/shim.js
CHANGED
|
@@ -8,6 +8,7 @@ import { Buffer } from '@craftzdog/react-native-buffer';
|
|
|
8
8
|
import process from 'process';
|
|
9
9
|
import 'react-native-url-polyfill/auto';
|
|
10
10
|
import { TextEncoder, TextDecoder } from 'text-encoding';
|
|
11
|
+
import structuredClone from '@ungap/structured-clone';
|
|
11
12
|
const setupProcessPolyfill = () => {
|
|
12
13
|
if (typeof globalThis.process === 'undefined') {
|
|
13
14
|
globalThis.process = process;
|
|
@@ -61,8 +62,14 @@ const setupTextEncodingPolyfills = () => {
|
|
|
61
62
|
globalThis.TextEncoder = TextEncoder;
|
|
62
63
|
globalThis.TextDecoder = TextDecoder;
|
|
63
64
|
};
|
|
65
|
+
const setupStructuredClonePolyfill = () => {
|
|
66
|
+
if (typeof globalThis.structuredClone === 'undefined') {
|
|
67
|
+
globalThis.structuredClone = structuredClone;
|
|
68
|
+
}
|
|
69
|
+
};
|
|
64
70
|
setupProcessPolyfill();
|
|
65
71
|
setupBufferPolyfill();
|
|
66
72
|
setupBase64Polyfills();
|
|
67
73
|
setupCryptoPolyfills();
|
|
68
74
|
setupTextEncodingPolyfills();
|
|
75
|
+
setupStructuredClonePolyfill();
|
package/package.json
CHANGED
|
@@ -1,50 +1,56 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@getpara/react-native-wallet",
|
|
3
|
-
"version": "2.0.0-dev.0",
|
|
4
3
|
"description": "Para Wallet for React Native",
|
|
5
|
-
"
|
|
4
|
+
"version": "2.0.0-dev.2",
|
|
6
5
|
"author": "Para Team <hello@getpara.com> (https://getpara.com)",
|
|
7
|
-
"main": "dist/index.js",
|
|
8
|
-
"module": "dist/index.js",
|
|
9
|
-
"types": "dist/index.d.ts",
|
|
10
|
-
"files": [
|
|
11
|
-
"dist",
|
|
12
|
-
"src",
|
|
13
|
-
"ios",
|
|
14
|
-
"android",
|
|
15
|
-
"cpp",
|
|
16
|
-
"*.podspec",
|
|
17
|
-
"signer.xcframework",
|
|
18
|
-
"signer.aar"
|
|
19
|
-
],
|
|
20
|
-
"scripts": {
|
|
21
|
-
"build": "rm -rf dist && tsc",
|
|
22
|
-
"compile-signer": "bash ./scripts/compileSigner.sh"
|
|
23
|
-
},
|
|
24
6
|
"dependencies": {
|
|
25
|
-
"@getpara/core-sdk": "2.0.0-dev.
|
|
26
|
-
"@getpara/user-management-client": "2.0.0-dev.
|
|
27
|
-
"@getpara/web-sdk": "2.0.0-dev.
|
|
7
|
+
"@getpara/core-sdk": "2.0.0-dev.2",
|
|
8
|
+
"@getpara/user-management-client": "2.0.0-dev.2",
|
|
9
|
+
"@getpara/web-sdk": "2.0.0-dev.2",
|
|
28
10
|
"@peculiar/webcrypto": "^1.5.0",
|
|
29
|
-
"@
|
|
11
|
+
"@ungap/structured-clone": "1.3.0",
|
|
30
12
|
"node-forge": "1.3.1",
|
|
31
13
|
"react-native-url-polyfill": "2.0.0",
|
|
32
14
|
"text-encoding": "0.7.0"
|
|
33
15
|
},
|
|
34
16
|
"devDependencies": {
|
|
35
17
|
"@craftzdog/react-native-buffer": "6.0.5",
|
|
36
|
-
"@react-native-async-storage/async-storage": "2.1.
|
|
18
|
+
"@react-native-async-storage/async-storage": "2.1.2",
|
|
37
19
|
"@types/node-forge": "1.3.1",
|
|
38
20
|
"@types/react": "^18.0.31",
|
|
39
21
|
"@types/react-native": "0.70.0",
|
|
40
22
|
"@types/text-encoding": "0.0.39",
|
|
41
|
-
"react-native-keychain": "
|
|
23
|
+
"react-native-keychain": "10.0.0",
|
|
42
24
|
"react-native-modpow": "1.1.0",
|
|
43
|
-
"react-native-passkey": "3.
|
|
25
|
+
"react-native-passkey": "3.1.0",
|
|
44
26
|
"react-native-quick-base64": "2.1.2",
|
|
45
|
-
"react-native-quick-crypto": "0.7.
|
|
46
|
-
"typescript": "^5.
|
|
27
|
+
"react-native-quick-crypto": "0.7.12",
|
|
28
|
+
"typescript": "^5.8.3"
|
|
47
29
|
},
|
|
30
|
+
"exports": {
|
|
31
|
+
".": {
|
|
32
|
+
"default": "./dist/index.js"
|
|
33
|
+
},
|
|
34
|
+
"./shim": {
|
|
35
|
+
"default": "./dist/shim.js"
|
|
36
|
+
},
|
|
37
|
+
"./dist/shim.js": {
|
|
38
|
+
"default": "./dist/shim.js"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"files": [
|
|
42
|
+
"dist",
|
|
43
|
+
"src",
|
|
44
|
+
"ios",
|
|
45
|
+
"android",
|
|
46
|
+
"cpp",
|
|
47
|
+
"*.podspec",
|
|
48
|
+
"signer.xcframework",
|
|
49
|
+
"signer.aar"
|
|
50
|
+
],
|
|
51
|
+
"homepage": "https://getpara.com",
|
|
52
|
+
"main": "./dist/index.js",
|
|
53
|
+
"module": "./dist/index.js",
|
|
48
54
|
"peerDependencies": {
|
|
49
55
|
"@craftzdog/react-native-buffer": "*",
|
|
50
56
|
"@react-native-async-storage/async-storage": "*",
|
|
@@ -60,5 +66,32 @@
|
|
|
60
66
|
"publishConfig": {
|
|
61
67
|
"access": "public"
|
|
62
68
|
},
|
|
63
|
-
"
|
|
69
|
+
"react-native": {
|
|
70
|
+
".": "./dist/index.js",
|
|
71
|
+
"./shim": "./dist/shim.js",
|
|
72
|
+
"./dist/shim.js": "./dist/shim.js"
|
|
73
|
+
},
|
|
74
|
+
"scripts": {
|
|
75
|
+
"build": "rm -rf dist && tsc",
|
|
76
|
+
"compile-signer": "bash ./scripts/compileSigner.sh",
|
|
77
|
+
"test": "vitest run --coverage"
|
|
78
|
+
},
|
|
79
|
+
"sideEffects": [
|
|
80
|
+
"./dist/shim.js"
|
|
81
|
+
],
|
|
82
|
+
"types": "./dist/index.d.ts",
|
|
83
|
+
"typesVersions": {
|
|
84
|
+
"*": {
|
|
85
|
+
"shim": [
|
|
86
|
+
"./dist/shim.d.ts"
|
|
87
|
+
],
|
|
88
|
+
"dist/shim.js": [
|
|
89
|
+
"./dist/shim.d.ts"
|
|
90
|
+
],
|
|
91
|
+
"*": [
|
|
92
|
+
"./dist/index.d.ts"
|
|
93
|
+
]
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
"gitHead": "77d818539daa181c839a40f0ad5362af4058844e"
|
|
64
97
|
}
|
package/src/AsyncStorage.ts
CHANGED
|
@@ -1,28 +1,46 @@
|
|
|
1
1
|
import { StorageUtils } from '@getpara/web-sdk';
|
|
2
2
|
import RNAsyncStorage from '@react-native-async-storage/async-storage';
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
* Implements `StorageUtils` using React Native Async Storage.
|
|
6
|
-
*/
|
|
7
4
|
export class AsyncStorage implements StorageUtils {
|
|
8
|
-
async
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
5
|
+
async get(key: string): Promise<string | null> {
|
|
6
|
+
try {
|
|
7
|
+
return await RNAsyncStorage.getItem(key);
|
|
8
|
+
} catch (error) {
|
|
9
|
+
console.warn('Error retrieving stored item:', error);
|
|
10
|
+
return null;
|
|
14
11
|
}
|
|
15
12
|
}
|
|
16
13
|
|
|
17
|
-
async
|
|
18
|
-
|
|
14
|
+
async set(key: string, value: string): Promise<void> {
|
|
15
|
+
try {
|
|
16
|
+
await RNAsyncStorage.setItem(key, value);
|
|
17
|
+
} catch (error) {
|
|
18
|
+
console.warn(`Error storing key ${key}:`, error);
|
|
19
|
+
}
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
async removeItem(key: string): Promise<void> {
|
|
22
|
-
|
|
23
|
+
try {
|
|
24
|
+
await RNAsyncStorage.removeItem(key);
|
|
25
|
+
} catch (error) {
|
|
26
|
+
console.warn(`Error removing key ${key}:`, error);
|
|
27
|
+
}
|
|
23
28
|
}
|
|
24
29
|
|
|
25
|
-
async
|
|
26
|
-
|
|
30
|
+
async clear(prefix: string): Promise<void> {
|
|
31
|
+
try {
|
|
32
|
+
const keys = await RNAsyncStorage.getAllKeys();
|
|
33
|
+
for (const key of keys) {
|
|
34
|
+
if (key.startsWith(prefix)) {
|
|
35
|
+
try {
|
|
36
|
+
await RNAsyncStorage.removeItem(key);
|
|
37
|
+
} catch (error) {
|
|
38
|
+
console.warn(`Error clearing key ${key}:`, error);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
} catch (error) {
|
|
43
|
+
console.warn(`Error getting keys for prefix ${prefix}:`, error);
|
|
44
|
+
}
|
|
27
45
|
}
|
|
28
46
|
}
|
package/src/KeychainStorage.ts
CHANGED
|
@@ -2,21 +2,7 @@ import { StorageUtils } from '@getpara/web-sdk';
|
|
|
2
2
|
import Keychain from 'react-native-keychain';
|
|
3
3
|
|
|
4
4
|
const USERNAME = '@CAPSULE';
|
|
5
|
-
const KEYCHAIN_USER_CANCELLED_ERRORS = [
|
|
6
|
-
'user canceled the operation',
|
|
7
|
-
'error: code: 13, msg: cancel',
|
|
8
|
-
'error: code: 10, msg: fingerprint operation canceled by the user',
|
|
9
|
-
];
|
|
10
5
|
|
|
11
|
-
function isUserCancelledError(error: Error) {
|
|
12
|
-
return KEYCHAIN_USER_CANCELLED_ERRORS.some(userCancelledError =>
|
|
13
|
-
error.toString().toLowerCase().includes(userCancelledError),
|
|
14
|
-
);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Implements `StorageUtils` using React Native `Keychain`.
|
|
19
|
-
*/
|
|
20
6
|
export class KeychainStorage implements StorageUtils {
|
|
21
7
|
async get(key: string): Promise<string | null> {
|
|
22
8
|
try {
|
|
@@ -28,32 +14,49 @@ export class KeychainStorage implements StorageUtils {
|
|
|
28
14
|
}
|
|
29
15
|
return item.password;
|
|
30
16
|
} catch (error) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
throw new Error('Error retrieving stored item ' + error.message);
|
|
34
|
-
}
|
|
35
|
-
throw error;
|
|
17
|
+
console.warn('Error retrieving stored item:', error);
|
|
18
|
+
return null;
|
|
36
19
|
}
|
|
37
20
|
}
|
|
21
|
+
|
|
38
22
|
async set(key: string, value: string): Promise<void> {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
23
|
+
try {
|
|
24
|
+
const result = await Keychain.setGenericPassword(USERNAME, value, {
|
|
25
|
+
service: key,
|
|
26
|
+
accessible: Keychain.ACCESSIBLE.WHEN_UNLOCKED,
|
|
27
|
+
securityLevel: Keychain.SECURITY_LEVEL.ANY,
|
|
28
|
+
storage: Keychain.STORAGE_TYPE.AES_GCM_NO_AUTH,
|
|
29
|
+
});
|
|
30
|
+
if (!result) {
|
|
31
|
+
console.warn(`Failed to store key ${key}`);
|
|
32
|
+
}
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.warn(`Error storing key ${key}:`, error);
|
|
46
35
|
}
|
|
47
36
|
}
|
|
37
|
+
|
|
48
38
|
async removeItem(key: string): Promise<void> {
|
|
49
|
-
|
|
39
|
+
try {
|
|
40
|
+
await Keychain.resetGenericPassword({ service: key });
|
|
41
|
+
} catch (error) {
|
|
42
|
+
console.warn(`Error removing key ${key}:`, error);
|
|
43
|
+
}
|
|
50
44
|
}
|
|
45
|
+
|
|
51
46
|
async clear(prefix: string): Promise<void> {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
47
|
+
try {
|
|
48
|
+
const services = await Keychain.getAllGenericPasswordServices();
|
|
49
|
+
for (const key of services) {
|
|
50
|
+
if (key && key.startsWith(prefix)) {
|
|
51
|
+
try {
|
|
52
|
+
await Keychain.resetGenericPassword({ service: key });
|
|
53
|
+
} catch (error) {
|
|
54
|
+
console.warn(`Error clearing key ${key}:`, error);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
56
57
|
}
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.warn(`Error getting services for prefix ${prefix}:`, error);
|
|
57
60
|
}
|
|
58
61
|
}
|
|
59
62
|
}
|
package/src/config.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
AuthStateSignup,
|
|
2
3
|
ConstructorOpts,
|
|
3
4
|
ParaCore,
|
|
4
5
|
Environment,
|
|
@@ -12,7 +13,6 @@ import {
|
|
|
12
13
|
getSHA256HashHex,
|
|
13
14
|
parseCredentialCreationRes,
|
|
14
15
|
} from '@getpara/web-sdk';
|
|
15
|
-
import * as Sentry from '@sentry/react-native';
|
|
16
16
|
|
|
17
17
|
import { ReactNativeUtils } from './ReactNativeUtils.js';
|
|
18
18
|
import {
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
PasskeyGetRequest,
|
|
23
23
|
PasskeyGetResult,
|
|
24
24
|
} from 'react-native-passkey';
|
|
25
|
-
import {
|
|
25
|
+
import { CurrentWalletIds, PublicKeyStatus, TWalletScheme } from '@getpara/user-management-client';
|
|
26
26
|
import { setEnv } from '../config.js';
|
|
27
27
|
import base64url from 'base64url';
|
|
28
28
|
import { webcrypto } from 'crypto';
|
|
@@ -38,6 +38,8 @@ const RS256_ALGORITHM = -257;
|
|
|
38
38
|
* const para = new ParaMobile(Environment.BETA, "api_key");
|
|
39
39
|
*/
|
|
40
40
|
export class ParaMobile extends ParaCore {
|
|
41
|
+
isNativePasskey = true;
|
|
42
|
+
|
|
41
43
|
private relyingPartyId: string;
|
|
42
44
|
/**
|
|
43
45
|
* Creates an instance of ParaMobile.
|
|
@@ -49,15 +51,6 @@ export class ParaMobile extends ParaCore {
|
|
|
49
51
|
constructor(env: Environment, apiKey: string, relyingPartyId?: string, opts?: ConstructorOpts) {
|
|
50
52
|
super(env, apiKey, opts);
|
|
51
53
|
|
|
52
|
-
// starting with non-prod to see what kind of errors we get and if sensitive data is tracked
|
|
53
|
-
// will turn on in prod after monitoring
|
|
54
|
-
if (env !== Environment.PROD && env !== Environment.DEV) {
|
|
55
|
-
Sentry.init({
|
|
56
|
-
environment: env.toLowerCase(),
|
|
57
|
-
dsn: 'https://59cea0cfbbb30a646c4e9f2feea06da4@o4504568036720640.ingest.us.sentry.io/4508850922323968',
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
|
|
61
54
|
setEnv(env);
|
|
62
55
|
|
|
63
56
|
if (relyingPartyId) {
|
|
@@ -79,44 +72,32 @@ export class ParaMobile extends ParaCore {
|
|
|
79
72
|
}
|
|
80
73
|
}
|
|
81
74
|
|
|
75
|
+
protected async ready() {
|
|
76
|
+
this.isReady = true;
|
|
77
|
+
}
|
|
78
|
+
|
|
82
79
|
protected getPlatformUtils(): PlatformUtils {
|
|
83
80
|
return new ReactNativeUtils();
|
|
84
81
|
}
|
|
85
82
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
* @param {string} verificationCode - The verification code sent to the email.
|
|
89
|
-
* @returns {Promise<string>} The biometrics ID.
|
|
90
|
-
*/
|
|
91
|
-
async verifyEmailBiometricsId({ verificationCode }: { verificationCode: string }): Promise<string> {
|
|
92
|
-
const webAuthCreateUrl = await super.verifyEmail({ verificationCode });
|
|
93
|
-
const segments = webAuthCreateUrl.split('/');
|
|
94
|
-
const segments2 = segments[segments.length - 1]!.split('?');
|
|
95
|
-
const biometricsId = segments2[0]!;
|
|
96
|
-
|
|
97
|
-
return biometricsId;
|
|
83
|
+
async isPasskeySupported(): Promise<boolean> {
|
|
84
|
+
return Passkey.isSupported();
|
|
98
85
|
}
|
|
99
86
|
|
|
100
|
-
/**
|
|
101
|
-
* Verifies a phone number and returns the biometrics ID.
|
|
102
|
-
* @param {string} verificationCode - The verification code sent to the phone.
|
|
103
|
-
* @returns {Promise<string>} The biometrics ID.
|
|
104
|
-
*/
|
|
105
|
-
async verifyPhoneBiometricsId({ verificationCode }: { verificationCode: string }): Promise<string> {
|
|
106
|
-
const webAuthCreateUrl = await super.verifyPhone({ verificationCode });
|
|
107
|
-
const segments = webAuthCreateUrl.split('/');
|
|
108
|
-
const segments2 = segments[segments.length - 1]!.split('?');
|
|
109
|
-
const biometricsId = segments2[0]!;
|
|
110
|
-
|
|
111
|
-
return biometricsId;
|
|
112
|
-
}
|
|
113
87
|
/**
|
|
114
88
|
* Registers a passkey for the user.
|
|
115
89
|
* @param {Auth<'email'> | Auth<'phone'>} auth - The user's authentication details
|
|
116
90
|
* @param {string} biometricsId - The biometrics ID obtained from verification.
|
|
117
91
|
* @returns {Promise<void>}
|
|
118
92
|
*/
|
|
119
|
-
async registerPasskey(
|
|
93
|
+
async registerPasskey(authState: AuthStateSignup) {
|
|
94
|
+
if (!authState.passkeyId) {
|
|
95
|
+
throw new Error('Passkey ID not found. Make sure you have enabled passkey logins in the Para Developer Portal.');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const userId = this.assertUserId();
|
|
99
|
+
const authInfo = this.assertIsAuthSet();
|
|
100
|
+
|
|
120
101
|
if (!webcrypto || !webcrypto.getRandomValues) {
|
|
121
102
|
throw new Error('Web crypto is not available. Ensure you have imported the shim from @getpara/react-native-wallet.');
|
|
122
103
|
}
|
|
@@ -124,8 +105,6 @@ export class ParaMobile extends ParaCore {
|
|
|
124
105
|
webcrypto.getRandomValues(userHandle);
|
|
125
106
|
const userHandleEncoded = base64url.encode(userHandle as any);
|
|
126
107
|
|
|
127
|
-
const { identifier: displayIdentifier } = extractAuthInfo(auth, { isRequired: true });
|
|
128
|
-
|
|
129
108
|
const requestJson: PasskeyCreateRequest = {
|
|
130
109
|
authenticatorSelection: {
|
|
131
110
|
authenticatorAttachment: 'platform' as any,
|
|
@@ -139,8 +118,8 @@ export class ParaMobile extends ParaCore {
|
|
|
139
118
|
},
|
|
140
119
|
user: {
|
|
141
120
|
id: userHandleEncoded,
|
|
142
|
-
name:
|
|
143
|
-
displayName:
|
|
121
|
+
name: authInfo.identifier,
|
|
122
|
+
displayName: authInfo.identifier,
|
|
144
123
|
},
|
|
145
124
|
pubKeyCredParams: [
|
|
146
125
|
{
|
|
@@ -175,7 +154,7 @@ export class ParaMobile extends ParaCore {
|
|
|
175
154
|
const encryptedPrivateKeyHex = await encryptPrivateKey(keyPair, userHandleEncoded);
|
|
176
155
|
|
|
177
156
|
const { partnerId } = await this.ctx.client.touchSession();
|
|
178
|
-
await this.ctx.client.patchSessionPublicKey(partnerId,
|
|
157
|
+
await this.ctx.client.patchSessionPublicKey(partnerId, userId, authState.passkeyId, {
|
|
179
158
|
publicKey: resultJson.id,
|
|
180
159
|
sigDerivedPublicKey: publicKeyHex,
|
|
181
160
|
cosePublicKey,
|
|
@@ -183,12 +162,7 @@ export class ParaMobile extends ParaCore {
|
|
|
183
162
|
status: PublicKeyStatus.COMPLETE,
|
|
184
163
|
});
|
|
185
164
|
|
|
186
|
-
await this.ctx.client.uploadEncryptedWalletPrivateKey(
|
|
187
|
-
this.getUserId()!,
|
|
188
|
-
encryptedPrivateKeyHex,
|
|
189
|
-
encryptionKeyHash,
|
|
190
|
-
resultJson.id,
|
|
191
|
-
);
|
|
165
|
+
await this.ctx.client.uploadEncryptedWalletPrivateKey(userId, encryptedPrivateKeyHex, encryptionKeyHash, resultJson.id);
|
|
192
166
|
}
|
|
193
167
|
|
|
194
168
|
/**
|
|
@@ -196,12 +170,11 @@ export class ParaMobile extends ParaCore {
|
|
|
196
170
|
* @param {AuthParams} params - The authentication parameters.
|
|
197
171
|
* @returns {Promise<void>}
|
|
198
172
|
*/
|
|
199
|
-
async
|
|
200
|
-
|
|
173
|
+
async loginWithPasskey(): Promise<void> {
|
|
174
|
+
this.assertIsAuthSet();
|
|
175
|
+
const userId = this.assertUserId();
|
|
201
176
|
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
const { challenge, allowedPublicKeys } = await this.ctx.client.getWebChallenge(authInfo.auth);
|
|
177
|
+
const { challenge, allowedPublicKeys } = await this.ctx.client.getWebChallenge({ userId });
|
|
205
178
|
|
|
206
179
|
const requestJson: PasskeyGetRequest = {
|
|
207
180
|
challenge,
|
|
@@ -220,7 +193,7 @@ export class ParaMobile extends ParaCore {
|
|
|
220
193
|
resultJson = result;
|
|
221
194
|
}
|
|
222
195
|
|
|
223
|
-
const { partnerId } = await this.ctx.client.touchSession();
|
|
196
|
+
const { partnerId, sessionLookupId } = await this.ctx.client.touchSession();
|
|
224
197
|
const publicKey = resultJson.id;
|
|
225
198
|
const verifyWebChallengeResult = await this.ctx.client.verifyWebChallenge(partnerId, {
|
|
226
199
|
publicKey,
|
|
@@ -231,22 +204,8 @@ export class ParaMobile extends ParaCore {
|
|
|
231
204
|
},
|
|
232
205
|
});
|
|
233
206
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
await this.setUserId(userId);
|
|
237
|
-
|
|
238
|
-
const { user } = await this.ctx.client.getUser(userId);
|
|
239
|
-
|
|
240
|
-
if (user.phone) {
|
|
241
|
-
await this.setPhoneNumber(user.phone.number, user.phone.countryCode);
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
if (user.email) {
|
|
245
|
-
await this.setEmail(user.email);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
if (user.farcasterUsername) {
|
|
249
|
-
await this.setFarcasterUsername(user.farcasterUsername);
|
|
207
|
+
if (userId !== verifyWebChallengeResult.data.userId) {
|
|
208
|
+
throw new Error('User ID mismatch');
|
|
250
209
|
}
|
|
251
210
|
|
|
252
211
|
const encryptedSharesResult = await this.ctx.client.getBiometricKeyshares(userId, resultJson.id);
|
|
@@ -273,7 +232,7 @@ export class ParaMobile extends ParaCore {
|
|
|
273
232
|
decryptedShares = await decryptPrivateKeyAndDecryptShare(
|
|
274
233
|
resultJson.response.userHandle,
|
|
275
234
|
encryptedSharesResult.data.keyShares,
|
|
276
|
-
encryptedPrivateKeys[0]
|
|
235
|
+
encryptedPrivateKeys[0]!.encryptedPrivateKey,
|
|
277
236
|
);
|
|
278
237
|
}
|
|
279
238
|
|
|
@@ -288,11 +247,21 @@ export class ParaMobile extends ParaCore {
|
|
|
288
247
|
signer: decryptedShare.signer,
|
|
289
248
|
address: desiredWallet.address || undefined,
|
|
290
249
|
publicKey: desiredWallet.publicKey || undefined,
|
|
291
|
-
scheme: desiredWallet.scheme as
|
|
250
|
+
scheme: desiredWallet.scheme as TWalletScheme,
|
|
292
251
|
type: desiredWallet.type || undefined,
|
|
293
252
|
};
|
|
294
253
|
}
|
|
295
254
|
|
|
255
|
+
const currentWalletIds: CurrentWalletIds = {};
|
|
256
|
+
for (const wallet of Object.values(walletsToInsert)) {
|
|
257
|
+
const { id, type } = wallet;
|
|
258
|
+
const currentIdsForType = currentWalletIds[type || 'EVM'] || [];
|
|
259
|
+
currentWalletIds[type || 'EVM'] = [...currentIdsForType, id];
|
|
260
|
+
}
|
|
261
|
+
|
|
296
262
|
await this.setWallets(walletsToInsert);
|
|
263
|
+
await this.setCurrentWalletIds(currentWalletIds, {
|
|
264
|
+
sessionLookupId,
|
|
265
|
+
});
|
|
297
266
|
}
|
|
298
267
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { PlatformUtils, TPregenIdentifierType } from '@getpara/web-sdk';
|
|
2
2
|
import { Ctx } from '@getpara/web-sdk';
|
|
3
3
|
import { SignatureRes } from '@getpara/web-sdk';
|
|
4
|
-
import { BackupKitEmailProps, KeyShareType,
|
|
4
|
+
import { BackupKitEmailProps, KeyShareType, TWalletType, SDKType } from '@getpara/user-management-client';
|
|
5
5
|
import { NativeModules } from 'react-native';
|
|
6
6
|
|
|
7
7
|
import { AsyncStorage } from '../AsyncStorage.js';
|
|
@@ -55,6 +55,7 @@ async function sendTransactionRequest(
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
export class ReactNativeUtils implements PlatformUtils {
|
|
58
|
+
sdkType: SDKType = 'REACT_NATIVE';
|
|
58
59
|
disableProviderModal?: boolean | undefined;
|
|
59
60
|
localStorage = new AsyncStorage();
|
|
60
61
|
sessionStorage = new AsyncStorage();
|
|
@@ -69,7 +70,7 @@ export class ReactNativeUtils implements PlatformUtils {
|
|
|
69
70
|
async keygen(
|
|
70
71
|
ctx: Ctx,
|
|
71
72
|
userId: string,
|
|
72
|
-
type: Exclude<
|
|
73
|
+
type: Exclude<TWalletType, 'SOLANA'>,
|
|
73
74
|
_secretKey: string | null,
|
|
74
75
|
_sessionCookie: string,
|
|
75
76
|
_emailProps?: BackupKitEmailProps | undefined,
|
|
@@ -77,7 +78,7 @@ export class ReactNativeUtils implements PlatformUtils {
|
|
|
77
78
|
const { walletId, protocolId } = await ctx.client.createWallet(userId, {
|
|
78
79
|
type,
|
|
79
80
|
useTwoSigners: true,
|
|
80
|
-
scheme: ctx.useDKLS ?
|
|
81
|
+
scheme: ctx.useDKLS ? 'DKLS' : 'CGGMP',
|
|
81
82
|
});
|
|
82
83
|
|
|
83
84
|
if (ctx.mpcComputationClient && !ctx.useDKLS) {
|
|
@@ -220,8 +221,8 @@ export class ReactNativeUtils implements PlatformUtils {
|
|
|
220
221
|
walletId: string;
|
|
221
222
|
}> {
|
|
222
223
|
const { walletId, protocolId } = await ctx.client.createWallet(userId, {
|
|
223
|
-
scheme:
|
|
224
|
-
type:
|
|
224
|
+
scheme: 'ED25519',
|
|
225
|
+
type: 'SOLANA',
|
|
225
226
|
});
|
|
226
227
|
|
|
227
228
|
const signer = await ParaSignerModule.ed25519CreateAccount(walletId, protocolId);
|
|
@@ -240,8 +241,8 @@ export class ReactNativeUtils implements PlatformUtils {
|
|
|
240
241
|
const { walletId, protocolId } = await ctx.client.createPregenWallet({
|
|
241
242
|
pregenIdentifier,
|
|
242
243
|
pregenIdentifierType,
|
|
243
|
-
scheme:
|
|
244
|
-
type:
|
|
244
|
+
scheme: 'ED25519',
|
|
245
|
+
type: 'SOLANA',
|
|
245
246
|
});
|
|
246
247
|
|
|
247
248
|
const signer = await ParaSignerModule.ed25519CreateAccount(walletId, protocolId);
|
|
@@ -256,7 +257,7 @@ export class ReactNativeUtils implements PlatformUtils {
|
|
|
256
257
|
base64Bytes: string,
|
|
257
258
|
_sessionCookie: string,
|
|
258
259
|
): Promise<SignatureRes> {
|
|
259
|
-
const { protocolId } = await ctx.client.preSignMessage(userId, walletId, base64Bytes,
|
|
260
|
+
const { protocolId } = await ctx.client.preSignMessage(userId, walletId, base64Bytes, 'ED25519');
|
|
260
261
|
|
|
261
262
|
const base64Sig = await ParaSignerModule.ed25519Sign(protocolId, share, base64Bytes);
|
|
262
263
|
return { signature: base64Sig };
|
package/src/shim.js
CHANGED
|
@@ -8,6 +8,7 @@ import { Buffer } from '@craftzdog/react-native-buffer';
|
|
|
8
8
|
import process from 'process';
|
|
9
9
|
import 'react-native-url-polyfill/auto';
|
|
10
10
|
import { TextEncoder, TextDecoder } from 'text-encoding';
|
|
11
|
+
import structuredClone from '@ungap/structured-clone';
|
|
11
12
|
|
|
12
13
|
const setupProcessPolyfill = () => {
|
|
13
14
|
if (typeof globalThis.process === 'undefined') {
|
|
@@ -67,8 +68,15 @@ const setupTextEncodingPolyfills = () => {
|
|
|
67
68
|
globalThis.TextDecoder = TextDecoder;
|
|
68
69
|
};
|
|
69
70
|
|
|
71
|
+
const setupStructuredClonePolyfill = () => {
|
|
72
|
+
if (typeof globalThis.structuredClone === 'undefined') {
|
|
73
|
+
globalThis.structuredClone = structuredClone;
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
70
77
|
setupProcessPolyfill();
|
|
71
78
|
setupBufferPolyfill();
|
|
72
79
|
setupBase64Polyfills();
|
|
73
80
|
setupCryptoPolyfills();
|
|
74
81
|
setupTextEncodingPolyfills();
|
|
82
|
+
setupStructuredClonePolyfill();
|
|
File without changes
|