@oxyhq/services 5.16.33 → 5.16.35
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 +26 -8
- package/lib/commonjs/core/OxyServices.base.js.map +1 -1
- package/lib/commonjs/core/mixins/OxyServices.user.js.map +1 -1
- package/lib/commonjs/core/mixins/OxyServices.utility.js.map +1 -1
- package/lib/commonjs/core/services/AuthService.js +156 -0
- package/lib/commonjs/core/services/AuthService.js.map +1 -0
- package/lib/commonjs/core/services/SessionService.js +1 -2
- package/lib/commonjs/core/services/SessionService.js.map +1 -1
- package/lib/commonjs/core/services/SessionTransportService.js +64 -0
- package/lib/commonjs/core/services/SessionTransportService.js.map +1 -0
- package/lib/commonjs/core/services/TokenService.js +9 -17
- package/lib/commonjs/core/services/TokenService.js.map +1 -1
- package/lib/commonjs/core/services/UserService.js +123 -0
- package/lib/commonjs/core/services/UserService.js.map +1 -0
- package/lib/commonjs/core/services/index.js +34 -0
- package/lib/commonjs/core/services/index.js.map +1 -0
- package/lib/commonjs/crypto/index.js.map +1 -1
- package/lib/commonjs/crypto/keyManager.js +3 -2
- package/lib/commonjs/crypto/keyManager.js.map +1 -1
- package/lib/commonjs/crypto/signatureService.js +28 -122
- package/lib/commonjs/crypto/signatureService.js.map +1 -1
- package/lib/commonjs/index.js +12 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/models/interfaces.js +11 -11
- package/lib/commonjs/models/interfaces.js.map +1 -1
- package/lib/commonjs/shared/crypto/messageBuilders.js +79 -0
- package/lib/commonjs/shared/crypto/messageBuilders.js.map +1 -0
- package/lib/commonjs/shared/crypto/platform.js +118 -0
- package/lib/commonjs/shared/crypto/platform.js.map +1 -0
- package/lib/commonjs/shared/crypto/signature.js +191 -0
- package/lib/commonjs/shared/crypto/signature.js.map +1 -0
- package/lib/commonjs/shared/index.js +94 -0
- package/lib/commonjs/shared/index.js.map +1 -0
- package/lib/commonjs/shared/models/index.js +2 -0
- package/lib/commonjs/shared/models/index.js.map +1 -0
- package/lib/commonjs/shared/transport/index.js +260 -0
- package/lib/commonjs/shared/transport/index.js.map +1 -0
- package/lib/commonjs/shared/utils/index.js +82 -0
- package/lib/commonjs/shared/utils/index.js.map +1 -0
- package/lib/commonjs/ui/context/OxyContext.js +4 -40
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js +23 -61
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +1 -1
- package/lib/commonjs/ui/context/hooks/useLanguageManagement.js.map +1 -1
- package/lib/commonjs/ui/hooks/queries/useServicesQueries.js +4 -12
- package/lib/commonjs/ui/hooks/queries/useServicesQueries.js.map +1 -1
- package/lib/commonjs/ui/hooks/useLanguageManagement.js.map +1 -1
- package/lib/commonjs/ui/hooks/useSessionManagement.js +0 -8
- package/lib/commonjs/ui/hooks/useSessionManagement.js.map +1 -1
- package/lib/commonjs/ui/index.js +2 -0
- package/lib/commonjs/ui/index.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/OxyAuthScreen.js +2 -11
- package/lib/commonjs/ui/screens/OxyAuthScreen.js.map +1 -1
- package/lib/commonjs/ui/utils/sessionHelpers.js +11 -26
- package/lib/commonjs/ui/utils/sessionHelpers.js.map +1 -1
- package/lib/commonjs/utils/sessionUtils.js +1 -8
- package/lib/commonjs/utils/sessionUtils.js.map +1 -1
- package/lib/module/core/OxyServices.base.js.map +1 -1
- package/lib/module/core/mixins/OxyServices.user.js.map +1 -1
- package/lib/module/core/mixins/OxyServices.utility.js.map +1 -1
- package/lib/module/core/services/AuthService.js +151 -0
- package/lib/module/core/services/AuthService.js.map +1 -0
- package/lib/module/core/services/SessionService.js +1 -2
- package/lib/module/core/services/SessionService.js.map +1 -1
- package/lib/module/core/services/SessionTransportService.js +59 -0
- package/lib/module/core/services/SessionTransportService.js.map +1 -0
- package/lib/module/core/services/TokenService.js +9 -17
- package/lib/module/core/services/TokenService.js.map +1 -1
- package/lib/module/core/services/UserService.js +118 -0
- package/lib/module/core/services/UserService.js.map +1 -0
- package/lib/module/core/services/index.js +16 -0
- package/lib/module/core/services/index.js.map +1 -0
- package/lib/module/crypto/index.js +9 -0
- package/lib/module/crypto/index.js.map +1 -1
- package/lib/module/crypto/keyManager.js +3 -2
- package/lib/module/crypto/keyManager.js.map +1 -1
- package/lib/module/crypto/signatureService.js +26 -122
- package/lib/module/crypto/signatureService.js.map +1 -1
- package/lib/module/index.js +2 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/models/interfaces.js +11 -11
- package/lib/module/models/interfaces.js.map +1 -1
- package/lib/module/shared/crypto/messageBuilders.js +70 -0
- package/lib/module/shared/crypto/messageBuilders.js.map +1 -0
- package/lib/module/shared/crypto/platform.js +112 -0
- package/lib/module/shared/crypto/platform.js.map +1 -0
- package/lib/module/shared/crypto/signature.js +186 -0
- package/lib/module/shared/crypto/signature.js.map +1 -0
- package/lib/module/shared/index.js +30 -0
- package/lib/module/shared/index.js.map +1 -0
- package/lib/module/shared/models/index.js +2 -0
- package/lib/module/shared/models/index.js.map +1 -0
- package/lib/module/shared/transport/index.js +254 -0
- package/lib/module/shared/transport/index.js.map +1 -0
- package/lib/module/shared/utils/index.js +74 -0
- package/lib/module/shared/utils/index.js.map +1 -0
- package/lib/module/ui/context/OxyContext.js +4 -40
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/context/hooks/useAuthOperations.js +23 -61
- package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -1
- package/lib/module/ui/context/hooks/useLanguageManagement.js.map +1 -1
- package/lib/module/ui/hooks/queries/useServicesQueries.js +5 -13
- package/lib/module/ui/hooks/queries/useServicesQueries.js.map +1 -1
- package/lib/module/ui/hooks/useLanguageManagement.js.map +1 -1
- package/lib/module/ui/hooks/useSessionManagement.js +0 -8
- package/lib/module/ui/hooks/useSessionManagement.js.map +1 -1
- package/lib/module/ui/index.js +1 -0
- package/lib/module/ui/index.js.map +1 -1
- package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/module/ui/screens/OxyAuthScreen.js +2 -11
- package/lib/module/ui/screens/OxyAuthScreen.js.map +1 -1
- package/lib/module/ui/utils/sessionHelpers.js +11 -26
- package/lib/module/ui/utils/sessionHelpers.js.map +1 -1
- package/lib/module/utils/sessionUtils.js +1 -8
- package/lib/module/utils/sessionUtils.js.map +1 -1
- package/lib/typescript/core/OxyServices.base.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.analytics.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.assets.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.auth.d.ts +1 -1
- package/lib/typescript/core/mixins/OxyServices.auth.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.developer.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.devices.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.karma.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.language.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.location.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.payment.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.privacy.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.security.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.user.d.ts +2 -1
- package/lib/typescript/core/mixins/OxyServices.user.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.utility.d.ts.map +1 -1
- package/lib/typescript/core/mixins/index.d.ts +13 -13
- package/lib/typescript/core/mixins/index.d.ts.map +1 -1
- package/lib/typescript/core/services/AuthService.d.ts +50 -0
- package/lib/typescript/core/services/AuthService.d.ts.map +1 -0
- package/lib/typescript/core/services/SessionService.d.ts +3 -5
- package/lib/typescript/core/services/SessionService.d.ts.map +1 -1
- package/lib/typescript/core/services/SessionTransportService.d.ts +31 -0
- package/lib/typescript/core/services/SessionTransportService.d.ts.map +1 -0
- package/lib/typescript/core/services/TokenService.d.ts +3 -8
- package/lib/typescript/core/services/TokenService.d.ts.map +1 -1
- package/lib/typescript/core/services/UserService.d.ts +39 -0
- package/lib/typescript/core/services/UserService.d.ts.map +1 -0
- package/lib/typescript/core/services/index.d.ts +13 -0
- package/lib/typescript/core/services/index.d.ts.map +1 -0
- package/lib/typescript/crypto/index.d.ts +9 -0
- package/lib/typescript/crypto/index.d.ts.map +1 -1
- package/lib/typescript/crypto/keyManager.d.ts.map +1 -1
- package/lib/typescript/crypto/signatureService.d.ts +10 -13
- package/lib/typescript/crypto/signatureService.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +2 -1
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/models/interfaces.d.ts +15 -69
- package/lib/typescript/models/interfaces.d.ts.map +1 -1
- package/lib/typescript/models/session.d.ts +2 -4
- package/lib/typescript/models/session.d.ts.map +1 -1
- package/lib/typescript/shared/crypto/messageBuilders.d.ts +38 -0
- package/lib/typescript/shared/crypto/messageBuilders.d.ts.map +1 -0
- package/lib/typescript/shared/crypto/platform.d.ts +54 -0
- package/lib/typescript/shared/crypto/platform.d.ts.map +1 -0
- package/lib/typescript/shared/crypto/signature.d.ts +72 -0
- package/lib/typescript/shared/crypto/signature.d.ts.map +1 -0
- package/lib/typescript/shared/index.d.ts +20 -0
- package/lib/typescript/shared/index.d.ts.map +1 -0
- package/lib/typescript/shared/models/index.d.ts +163 -0
- package/lib/typescript/shared/models/index.d.ts.map +1 -0
- package/lib/typescript/shared/transport/index.d.ts +73 -0
- package/lib/typescript/shared/transport/index.d.ts.map +1 -0
- package/lib/typescript/shared/utils/index.d.ts +28 -0
- package/lib/typescript/shared/utils/index.d.ts.map +1 -0
- package/lib/typescript/ui/context/OxyContext.d.ts +2 -1
- package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/ui/context/hooks/useAuthOperations.d.ts +2 -1
- package/lib/typescript/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
- package/lib/typescript/ui/context/hooks/useLanguageManagement.d.ts +2 -1
- package/lib/typescript/ui/context/hooks/useLanguageManagement.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/mutations/useAccountMutations.d.ts +1 -1
- package/lib/typescript/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/queries/useAccountQueries.d.ts +1 -1
- package/lib/typescript/ui/hooks/queries/useAccountQueries.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/queries/useServicesQueries.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/useLanguageManagement.d.ts +2 -1
- package/lib/typescript/ui/hooks/useLanguageManagement.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/useSessionManagement.d.ts +2 -1
- package/lib/typescript/ui/hooks/useSessionManagement.d.ts.map +1 -1
- package/lib/typescript/ui/index.d.ts +1 -1
- package/lib/typescript/ui/index.d.ts.map +1 -1
- package/lib/typescript/ui/screens/OxyAuthScreen.d.ts.map +1 -1
- package/lib/typescript/ui/stores/authStore.d.ts +1 -1
- package/lib/typescript/ui/stores/authStore.d.ts.map +1 -1
- package/lib/typescript/ui/utils/avatarUtils.d.ts +1 -1
- package/lib/typescript/ui/utils/avatarUtils.d.ts.map +1 -1
- package/lib/typescript/ui/utils/sessionHelpers.d.ts +2 -6
- package/lib/typescript/ui/utils/sessionHelpers.d.ts.map +1 -1
- package/lib/typescript/utils/sessionUtils.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/core/OxyServices.base.ts +2 -1
- package/src/core/mixins/OxyServices.auth.ts +1 -1
- package/src/core/mixins/OxyServices.user.ts +2 -1
- package/src/core/mixins/OxyServices.utility.ts +2 -1
- package/src/core/services/AuthService.ts +153 -0
- package/src/core/services/SessionService.ts +3 -5
- package/src/core/services/SessionTransportService.ts +69 -0
- package/src/core/services/TokenService.ts +10 -18
- package/src/core/services/UserService.ts +125 -0
- package/src/core/services/index.ts +14 -0
- package/src/crypto/index.ts +9 -0
- package/src/crypto/keyManager.ts +3 -2
- package/src/crypto/signatureService.ts +43 -142
- package/src/index.ts +3 -2
- package/src/models/interfaces.ts +21 -74
- package/src/models/session.ts +3 -5
- package/src/shared/crypto/messageBuilders.ts +89 -0
- package/src/shared/crypto/platform.ts +140 -0
- package/src/shared/crypto/signature.ts +235 -0
- package/src/shared/index.ts +28 -0
- package/src/shared/models/index.ts +173 -0
- package/src/shared/transport/index.ts +349 -0
- package/src/shared/utils/index.ts +73 -0
- package/src/ui/context/OxyContext.tsx +22 -57
- package/src/ui/context/hooks/useAuthOperations.ts +33 -65
- package/src/ui/context/hooks/useLanguageManagement.ts +2 -1
- package/src/ui/hooks/auth/index.ts +0 -2
- package/src/ui/hooks/mutations/useAccountMutations.ts +1 -1
- package/src/ui/hooks/mutations/useServicesMutations.ts +1 -1
- package/src/ui/hooks/queries/useAccountQueries.ts +1 -1
- package/src/ui/hooks/queries/useServicesQueries.ts +3 -8
- package/src/ui/hooks/useLanguageManagement.ts +2 -1
- package/src/ui/hooks/useSessionManagement.ts +3 -9
- package/src/ui/index.ts +2 -1
- package/src/ui/screens/AccountSettingsScreen.tsx +6 -6
- package/src/ui/screens/AccountSwitcherScreen.tsx +1 -1
- package/src/ui/screens/OxyAuthScreen.tsx +2 -11
- package/src/ui/screens/ProfileScreen.tsx +1 -1
- package/src/ui/stores/authStore.ts +1 -1
- package/src/ui/types/navigation.ts +1 -1
- package/src/ui/utils/avatarUtils.ts +1 -1
- package/src/ui/utils/sessionHelpers.ts +15 -32
- package/src/utils/sessionUtils.ts +1 -8
|
@@ -3,77 +3,27 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Handles signing and verification of messages using ECDSA secp256k1.
|
|
5
5
|
* Used for authenticating requests and proving identity ownership.
|
|
6
|
+
*
|
|
7
|
+
* Note: This service handles SIGNING (requires private key access via KeyManager).
|
|
8
|
+
* For VERIFICATION, use the shared SignatureService from '../shared' instead.
|
|
6
9
|
*/
|
|
7
10
|
|
|
8
11
|
import { ec as EC } from 'elliptic';
|
|
9
12
|
import { KeyManager } from './keyManager';
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
+
import {
|
|
14
|
+
buildAuthMessage,
|
|
15
|
+
buildRegistrationMessage,
|
|
16
|
+
buildRequestMessage,
|
|
17
|
+
getCryptoAdapter,
|
|
18
|
+
SignatureService as SharedSignatureService,
|
|
19
|
+
isTimestampFresh,
|
|
20
|
+
type SignedMessage,
|
|
21
|
+
} from '../shared';
|
|
13
22
|
|
|
14
23
|
const ec = new EC('secp256k1');
|
|
15
24
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
*/
|
|
19
|
-
function isReactNative(): boolean {
|
|
20
|
-
return typeof navigator !== 'undefined' && navigator.product === 'ReactNative';
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Check if we're in a Node.js environment
|
|
25
|
-
*/
|
|
26
|
-
function isNodeJS(): boolean {
|
|
27
|
-
return typeof process !== 'undefined' && process.versions != null && process.versions.node != null;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Initialize expo-crypto module
|
|
32
|
-
*/
|
|
33
|
-
async function initExpoCrypto(): Promise<typeof import('expo-crypto')> {
|
|
34
|
-
if (!ExpoCrypto) {
|
|
35
|
-
ExpoCrypto = await import('expo-crypto');
|
|
36
|
-
}
|
|
37
|
-
return ExpoCrypto;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Compute SHA-256 hash of a string
|
|
42
|
-
*/
|
|
43
|
-
async function sha256(message: string): Promise<string> {
|
|
44
|
-
// In React Native, always use expo-crypto
|
|
45
|
-
if (isReactNative() || !isNodeJS()) {
|
|
46
|
-
const Crypto = await initExpoCrypto();
|
|
47
|
-
return Crypto.digestStringAsync(
|
|
48
|
-
Crypto.CryptoDigestAlgorithm.SHA256,
|
|
49
|
-
message
|
|
50
|
-
);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// In Node.js, use Node's crypto module
|
|
54
|
-
// Use Function constructor to prevent Metro bundler from statically analyzing this require
|
|
55
|
-
// This ensures the require is only evaluated in Node.js runtime, not during Metro bundling
|
|
56
|
-
try {
|
|
57
|
-
// eslint-disable-next-line @typescript-eslint/no-implied-eval
|
|
58
|
-
const getCrypto = new Function('return require("crypto")');
|
|
59
|
-
const crypto = getCrypto();
|
|
60
|
-
return crypto.createHash('sha256').update(message).digest('hex');
|
|
61
|
-
} catch (error) {
|
|
62
|
-
// Fallback to expo-crypto if Node crypto fails
|
|
63
|
-
const Crypto = await initExpoCrypto();
|
|
64
|
-
return Crypto.digestStringAsync(
|
|
65
|
-
Crypto.CryptoDigestAlgorithm.SHA256,
|
|
66
|
-
message
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export interface SignedMessage {
|
|
72
|
-
message: string;
|
|
73
|
-
signature: string;
|
|
74
|
-
publicKey: string;
|
|
75
|
-
timestamp: number;
|
|
76
|
-
}
|
|
25
|
+
// Re-export shared types
|
|
26
|
+
export type { SignedMessage } from '../shared';
|
|
77
27
|
|
|
78
28
|
export interface AuthChallenge {
|
|
79
29
|
challenge: string;
|
|
@@ -84,39 +34,23 @@ export interface AuthChallenge {
|
|
|
84
34
|
export class SignatureService {
|
|
85
35
|
/**
|
|
86
36
|
* Generate a random challenge string (for offline use)
|
|
87
|
-
* Uses
|
|
37
|
+
* Uses shared crypto adapter
|
|
88
38
|
*/
|
|
89
39
|
static async generateChallenge(): Promise<string> {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
.map((b: number) => b.toString(16).padStart(2, '0'))
|
|
96
|
-
.join('');
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Node.js fallback
|
|
100
|
-
try {
|
|
101
|
-
// eslint-disable-next-line @typescript-eslint/no-implied-eval
|
|
102
|
-
const getCrypto = new Function('return require("crypto")');
|
|
103
|
-
const crypto = getCrypto();
|
|
104
|
-
return crypto.randomBytes(32).toString('hex');
|
|
105
|
-
} catch (error) {
|
|
106
|
-
// Fallback to expo-crypto if Node crypto fails
|
|
107
|
-
const Crypto = await initExpoCrypto();
|
|
108
|
-
const randomBytes = await Crypto.getRandomBytesAsync(32);
|
|
109
|
-
return Array.from(randomBytes)
|
|
110
|
-
.map((b: number) => b.toString(16).padStart(2, '0'))
|
|
111
|
-
.join('');
|
|
112
|
-
}
|
|
40
|
+
const adapter = await getCryptoAdapter();
|
|
41
|
+
const randomBytes = await adapter.randomBytes(32);
|
|
42
|
+
return Array.from(randomBytes)
|
|
43
|
+
.map((b: number) => b.toString(16).padStart(2, '0'))
|
|
44
|
+
.join('');
|
|
113
45
|
}
|
|
114
46
|
|
|
115
47
|
/**
|
|
116
48
|
* Hash a message using SHA-256
|
|
49
|
+
* Uses shared crypto adapter
|
|
117
50
|
*/
|
|
118
51
|
static async hashMessage(message: string): Promise<string> {
|
|
119
|
-
|
|
52
|
+
const adapter = await getCryptoAdapter();
|
|
53
|
+
return adapter.sha256(message);
|
|
120
54
|
}
|
|
121
55
|
|
|
122
56
|
/**
|
|
@@ -129,7 +63,8 @@ export class SignatureService {
|
|
|
129
63
|
throw new Error('No identity found. Please create or import an identity first.');
|
|
130
64
|
}
|
|
131
65
|
|
|
132
|
-
const
|
|
66
|
+
const adapter = await getCryptoAdapter();
|
|
67
|
+
const messageHash = await adapter.sha256(message);
|
|
133
68
|
const signature = keyPair.sign(messageHash);
|
|
134
69
|
return signature.toDER('hex');
|
|
135
70
|
}
|
|
@@ -140,45 +75,18 @@ export class SignatureService {
|
|
|
140
75
|
*/
|
|
141
76
|
static async signWithKey(message: string, privateKey: string): Promise<string> {
|
|
142
77
|
const keyPair = ec.keyFromPrivate(privateKey);
|
|
143
|
-
const
|
|
78
|
+
const adapter = await getCryptoAdapter();
|
|
79
|
+
const messageHash = await adapter.sha256(message);
|
|
144
80
|
const signature = keyPair.sign(messageHash);
|
|
145
81
|
return signature.toDER('hex');
|
|
146
82
|
}
|
|
147
83
|
|
|
148
84
|
/**
|
|
149
85
|
* Verify a signature against a message and public key
|
|
86
|
+
* Uses shared SignatureService for verification
|
|
150
87
|
*/
|
|
151
88
|
static async verify(message: string, signature: string, publicKey: string): Promise<boolean> {
|
|
152
|
-
|
|
153
|
-
const key = ec.keyFromPublic(publicKey, 'hex');
|
|
154
|
-
const messageHash = await sha256(message);
|
|
155
|
-
return key.verify(messageHash, signature);
|
|
156
|
-
} catch {
|
|
157
|
-
return false;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Synchronous verification (for Node.js backend)
|
|
163
|
-
* Uses crypto module directly for hashing
|
|
164
|
-
* Note: This method should only be used in Node.js environments
|
|
165
|
-
*/
|
|
166
|
-
static verifySync(message: string, signature: string, publicKey: string): boolean {
|
|
167
|
-
try {
|
|
168
|
-
if (!isNodeJS()) {
|
|
169
|
-
// In React Native, use async verify instead
|
|
170
|
-
throw new Error('verifySync should only be used in Node.js. Use verify() in React Native.');
|
|
171
|
-
}
|
|
172
|
-
// Use Function constructor to prevent Metro bundler from statically analyzing this require
|
|
173
|
-
// eslint-disable-next-line @typescript-eslint/no-implied-eval
|
|
174
|
-
const getCrypto = new Function('return require("crypto")');
|
|
175
|
-
const crypto = getCrypto();
|
|
176
|
-
const key = ec.keyFromPublic(publicKey, 'hex');
|
|
177
|
-
const messageHash = crypto.createHash('sha256').update(message).digest('hex');
|
|
178
|
-
return key.verify(messageHash, signature);
|
|
179
|
-
} catch {
|
|
180
|
-
return false;
|
|
181
|
-
}
|
|
89
|
+
return SharedSignatureService.verify(message, signature, publicKey);
|
|
182
90
|
}
|
|
183
91
|
|
|
184
92
|
/**
|
|
@@ -205,6 +113,7 @@ export class SignatureService {
|
|
|
205
113
|
/**
|
|
206
114
|
* Verify a signed message object
|
|
207
115
|
* Checks both signature validity and timestamp freshness
|
|
116
|
+
* Uses shared SignatureService for verification
|
|
208
117
|
*/
|
|
209
118
|
static async verifySignedMessage(
|
|
210
119
|
signedMessage: SignedMessage,
|
|
@@ -213,14 +122,13 @@ export class SignatureService {
|
|
|
213
122
|
const { message, signature, publicKey, timestamp } = signedMessage;
|
|
214
123
|
|
|
215
124
|
// Check timestamp freshness
|
|
216
|
-
|
|
217
|
-
if (now - timestamp > maxAgeMs) {
|
|
125
|
+
if (!isTimestampFresh(timestamp, maxAgeMs)) {
|
|
218
126
|
return false;
|
|
219
127
|
}
|
|
220
128
|
|
|
221
129
|
// Verify signature
|
|
222
130
|
const messageWithTimestamp = `${message}:${timestamp}`;
|
|
223
|
-
return
|
|
131
|
+
return SharedSignatureService.verify(messageWithTimestamp, signature, publicKey);
|
|
224
132
|
}
|
|
225
133
|
|
|
226
134
|
/**
|
|
@@ -234,7 +142,7 @@ export class SignatureService {
|
|
|
234
142
|
}
|
|
235
143
|
|
|
236
144
|
const timestamp = Date.now();
|
|
237
|
-
const message =
|
|
145
|
+
const message = buildAuthMessage(publicKey, challenge, timestamp);
|
|
238
146
|
const signature = await SignatureService.sign(message);
|
|
239
147
|
|
|
240
148
|
return {
|
|
@@ -246,6 +154,7 @@ export class SignatureService {
|
|
|
246
154
|
|
|
247
155
|
/**
|
|
248
156
|
* Verify a challenge response
|
|
157
|
+
* Uses shared SignatureService for verification
|
|
249
158
|
*/
|
|
250
159
|
static async verifyChallengeResponse(
|
|
251
160
|
originalChallenge: string,
|
|
@@ -253,15 +162,13 @@ export class SignatureService {
|
|
|
253
162
|
maxAgeMs: number = 5 * 60 * 1000
|
|
254
163
|
): Promise<boolean> {
|
|
255
164
|
const { challenge: signature, publicKey, timestamp } = response;
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
const message = `auth:${publicKey}:${originalChallenge}:${timestamp}`;
|
|
264
|
-
return SignatureService.verify(message, signature, publicKey);
|
|
165
|
+
return SharedSignatureService.verifyChallengeResponse(
|
|
166
|
+
publicKey,
|
|
167
|
+
originalChallenge,
|
|
168
|
+
signature,
|
|
169
|
+
timestamp,
|
|
170
|
+
maxAgeMs
|
|
171
|
+
);
|
|
265
172
|
}
|
|
266
173
|
|
|
267
174
|
/**
|
|
@@ -276,7 +183,7 @@ export class SignatureService {
|
|
|
276
183
|
}
|
|
277
184
|
|
|
278
185
|
const timestamp = Date.now();
|
|
279
|
-
const message =
|
|
186
|
+
const message = buildRegistrationMessage(publicKey, timestamp);
|
|
280
187
|
const signature = await SignatureService.sign(message);
|
|
281
188
|
|
|
282
189
|
return {
|
|
@@ -301,13 +208,7 @@ export class SignatureService {
|
|
|
301
208
|
}
|
|
302
209
|
|
|
303
210
|
const timestamp = Date.now();
|
|
304
|
-
|
|
305
|
-
// Create canonical string representation
|
|
306
|
-
const sortedKeys = Object.keys(data).sort();
|
|
307
|
-
const canonicalParts = sortedKeys.map(key => `${key}:${JSON.stringify(data[key])}`);
|
|
308
|
-
const canonicalString = canonicalParts.join('|');
|
|
309
|
-
|
|
310
|
-
const message = `request:${publicKey}:${timestamp}:${canonicalString}`;
|
|
211
|
+
const message = buildRequestMessage(publicKey, timestamp, data);
|
|
311
212
|
const signature = await SignatureService.sign(message);
|
|
312
213
|
|
|
313
214
|
return {
|
package/src/index.ts
CHANGED
|
@@ -48,11 +48,12 @@ export {
|
|
|
48
48
|
} from './utils/languageUtils';
|
|
49
49
|
export type { LanguageMetadata } from './utils/languageUtils';
|
|
50
50
|
|
|
51
|
+
// Shared models and utilities (bundled for external consumers)
|
|
52
|
+
export * from './shared';
|
|
53
|
+
|
|
51
54
|
// Type exports
|
|
52
55
|
export type {
|
|
53
56
|
OxyConfig,
|
|
54
|
-
User,
|
|
55
|
-
LoginResponse,
|
|
56
57
|
Notification,
|
|
57
58
|
Wallet,
|
|
58
59
|
Transaction,
|
package/src/models/interfaces.ts
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Services Package Interfaces
|
|
3
|
+
*
|
|
4
|
+
* Package-specific interfaces. For shared models (User, Session, etc.),
|
|
5
|
+
* import directly from the shared module:
|
|
6
|
+
*
|
|
7
|
+
* import { User, LoginResponse, Session } from '../shared';
|
|
8
|
+
*/
|
|
9
|
+
|
|
1
10
|
export interface OxyConfig {
|
|
2
11
|
baseURL: string;
|
|
3
12
|
cloudURL?: string;
|
|
@@ -21,66 +30,8 @@ export interface OxyConfig {
|
|
|
21
30
|
onRequestError?: (url: string, method: string, error: Error) => void;
|
|
22
31
|
}
|
|
23
32
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
*
|
|
27
|
-
* IMPORTANT:
|
|
28
|
-
* - id: Public key (ECDSA secp256k1 public key, 130 hex characters) - CANONICAL USER IDENTITY
|
|
29
|
-
* - publicKey: Same as id (kept for backward compatibility and explicit clarity)
|
|
30
|
-
*
|
|
31
|
-
* publicKey is the canonical user identity across the ecosystem because it's:
|
|
32
|
-
* - Globally unique
|
|
33
|
-
* - Local-first (stored in Accounts app)
|
|
34
|
-
* - Avoids device-bound artifacts
|
|
35
|
-
*
|
|
36
|
-
* MongoDB _id remains internal to backend only and is not exposed in the User interface.
|
|
37
|
-
* Backend may use MongoDB ObjectId internally for database operations, but client always uses publicKey.
|
|
38
|
-
*/
|
|
39
|
-
export interface User {
|
|
40
|
-
id: string; // Public key - CANONICAL USER IDENTITY (130 hex chars for secp256k1)
|
|
41
|
-
publicKey: string; // Same as id (kept for backward compatibility)
|
|
42
|
-
username: string;
|
|
43
|
-
email?: string;
|
|
44
|
-
// Avatar file id (asset id)
|
|
45
|
-
avatar?: string;
|
|
46
|
-
// Privacy and security settings
|
|
47
|
-
privacySettings?: {
|
|
48
|
-
[key: string]: unknown;
|
|
49
|
-
};
|
|
50
|
-
name?: {
|
|
51
|
-
first?: string;
|
|
52
|
-
last?: string;
|
|
53
|
-
full?: string; // virtual, not stored in DB, returned by API
|
|
54
|
-
[key: string]: unknown;
|
|
55
|
-
};
|
|
56
|
-
bio?: string;
|
|
57
|
-
karma?: number;
|
|
58
|
-
location?: string;
|
|
59
|
-
website?: string;
|
|
60
|
-
createdAt?: string;
|
|
61
|
-
updatedAt?: string;
|
|
62
|
-
links?: Array<{
|
|
63
|
-
title?: string;
|
|
64
|
-
description?: string;
|
|
65
|
-
image?: string;
|
|
66
|
-
link: string;
|
|
67
|
-
}>;
|
|
68
|
-
// Social counts
|
|
69
|
-
_count?: {
|
|
70
|
-
followers?: number;
|
|
71
|
-
following?: number;
|
|
72
|
-
};
|
|
73
|
-
accountExpiresAfterInactivityDays?: number | null; // Days of inactivity before account expires (null = never expire)
|
|
74
|
-
[key: string]: unknown;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export interface LoginResponse {
|
|
78
|
-
accessToken?: string;
|
|
79
|
-
refreshToken?: string;
|
|
80
|
-
token?: string; // For backwards compatibility
|
|
81
|
-
user: User;
|
|
82
|
-
message?: string;
|
|
83
|
-
}
|
|
33
|
+
// Note: User and LoginResponse are in the shared module
|
|
34
|
+
// Import them directly: import { User, LoginResponse } from '../shared';
|
|
84
35
|
|
|
85
36
|
export interface Notification {
|
|
86
37
|
id: string;
|
|
@@ -153,17 +104,8 @@ export interface TransactionResponse {
|
|
|
153
104
|
transaction: Transaction;
|
|
154
105
|
}
|
|
155
106
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
limit: number;
|
|
159
|
-
offset: number;
|
|
160
|
-
hasMore: boolean;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
export interface SearchProfilesResponse {
|
|
164
|
-
data: User[];
|
|
165
|
-
pagination: PaginationInfo;
|
|
166
|
-
}
|
|
107
|
+
// Note: PaginationInfo and SearchProfilesResponse are in the shared module
|
|
108
|
+
// Import them directly: import { PaginationInfo, SearchProfilesResponse } from '../shared';
|
|
167
109
|
|
|
168
110
|
export interface KarmaRule {
|
|
169
111
|
id: string;
|
|
@@ -376,8 +318,6 @@ export interface AssetUrlResponse {
|
|
|
376
318
|
|
|
377
319
|
export interface AssetDeleteSummary {
|
|
378
320
|
fileId: string;
|
|
379
|
-
wouldDelete: boolean;
|
|
380
|
-
affectedApps: string[];
|
|
381
321
|
remainingLinks: number;
|
|
382
322
|
variants: string[];
|
|
383
323
|
}
|
|
@@ -495,6 +435,7 @@ export interface AssetUploadProgress {
|
|
|
495
435
|
}
|
|
496
436
|
|
|
497
437
|
// Device Session interfaces
|
|
438
|
+
// Note: User type should be imported from the shared module
|
|
498
439
|
export interface DeviceSession {
|
|
499
440
|
sessionId: string;
|
|
500
441
|
deviceId: string;
|
|
@@ -503,7 +444,13 @@ export interface DeviceSession {
|
|
|
503
444
|
lastActive: string;
|
|
504
445
|
expiresAt: string;
|
|
505
446
|
isCurrent: boolean;
|
|
506
|
-
user?:
|
|
447
|
+
user?: {
|
|
448
|
+
id: string;
|
|
449
|
+
publicKey: string;
|
|
450
|
+
username: string;
|
|
451
|
+
avatar?: string;
|
|
452
|
+
[key: string]: unknown;
|
|
453
|
+
}; // Partial User - import full User type from '../shared' if needed
|
|
507
454
|
createdAt?: string;
|
|
508
455
|
}
|
|
509
456
|
|
package/src/models/session.ts
CHANGED
|
@@ -2,17 +2,15 @@
|
|
|
2
2
|
* Client Session Model
|
|
3
3
|
*
|
|
4
4
|
* IMPORTANT:
|
|
5
|
-
* -
|
|
6
|
-
* -
|
|
7
|
-
* - Sessions are bound to the local identity (publicKey) stored in Accounts app
|
|
5
|
+
* - userId: MongoDB ObjectId (24 hex characters), never publicKey
|
|
6
|
+
* - Used for session management and user identification
|
|
8
7
|
*/
|
|
9
8
|
export interface ClientSession {
|
|
10
9
|
sessionId: string;
|
|
11
10
|
deviceId: string;
|
|
12
11
|
expiresAt: string;
|
|
13
12
|
lastActive: string;
|
|
14
|
-
|
|
15
|
-
userId?: string; // MongoDB ObjectId (internal backend reference, optional for compatibility)
|
|
13
|
+
userId?: string; // MongoDB ObjectId - PRIMARY IDENTIFIER (never publicKey)
|
|
16
14
|
isCurrent?: boolean;
|
|
17
15
|
}
|
|
18
16
|
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical Message Builders
|
|
3
|
+
*
|
|
4
|
+
* Creates standardized, canonical message formats for signing.
|
|
5
|
+
* These formats are used consistently across Accounts, Services SDK, and API.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { SignedMessage, AuthChallengeResponse } from '../models/index';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Build authentication message for challenge-response
|
|
12
|
+
* Format: auth:{publicKey}:{challenge}:{timestamp}
|
|
13
|
+
*/
|
|
14
|
+
export function buildAuthMessage(
|
|
15
|
+
publicKey: string,
|
|
16
|
+
challenge: string,
|
|
17
|
+
timestamp: number
|
|
18
|
+
): string {
|
|
19
|
+
return `auth:${publicKey}:${challenge}:${timestamp}`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Build registration message
|
|
24
|
+
* Format: oxy:register:{publicKey}:{timestamp}
|
|
25
|
+
*/
|
|
26
|
+
export function buildRegistrationMessage(
|
|
27
|
+
publicKey: string,
|
|
28
|
+
timestamp: number
|
|
29
|
+
): string {
|
|
30
|
+
return `oxy:register:${publicKey}:${timestamp}`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Build request signing message
|
|
35
|
+
* Format: request:{publicKey}:{timestamp}:{canonicalData}
|
|
36
|
+
*/
|
|
37
|
+
export function buildRequestMessage(
|
|
38
|
+
publicKey: string,
|
|
39
|
+
timestamp: number,
|
|
40
|
+
data: Record<string, unknown>
|
|
41
|
+
): string {
|
|
42
|
+
// Create canonical string representation
|
|
43
|
+
const sortedKeys = Object.keys(data).sort();
|
|
44
|
+
const canonicalParts = sortedKeys.map(key => `${key}:${JSON.stringify(data[key])}`);
|
|
45
|
+
const canonicalString = canonicalParts.join('|');
|
|
46
|
+
|
|
47
|
+
return `request:${publicKey}:${timestamp}:${canonicalString}`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Create canonical data representation for signing
|
|
52
|
+
* Sorts keys and creates a consistent string representation
|
|
53
|
+
*/
|
|
54
|
+
export function canonicalizeData(data: Record<string, unknown>): string {
|
|
55
|
+
const sortedKeys = Object.keys(data).sort();
|
|
56
|
+
const canonicalParts = sortedKeys.map(key => `${key}:${JSON.stringify(data[key])}`);
|
|
57
|
+
return canonicalParts.join('|');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Build auth challenge response payload
|
|
62
|
+
* Helper to construct the signed challenge response
|
|
63
|
+
*/
|
|
64
|
+
export function buildAuthChallengeResponse(
|
|
65
|
+
publicKey: string,
|
|
66
|
+
challenge: string,
|
|
67
|
+
signature: string,
|
|
68
|
+
timestamp: number
|
|
69
|
+
): AuthChallengeResponse {
|
|
70
|
+
return {
|
|
71
|
+
challenge,
|
|
72
|
+
publicKey,
|
|
73
|
+
signature,
|
|
74
|
+
timestamp,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Validate timestamp freshness
|
|
80
|
+
* Ensures signed messages are not too old
|
|
81
|
+
*/
|
|
82
|
+
export function isTimestampFresh(
|
|
83
|
+
timestamp: number,
|
|
84
|
+
maxAgeMs: number = 5 * 60 * 1000 // 5 minutes default
|
|
85
|
+
): boolean {
|
|
86
|
+
const now = Date.now();
|
|
87
|
+
return (now - timestamp) <= maxAgeMs && timestamp <= now;
|
|
88
|
+
}
|
|
89
|
+
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Platform Detection and Adapters
|
|
3
|
+
*
|
|
4
|
+
* Provides environment detection and platform-specific crypto adapters
|
|
5
|
+
* to support Node.js, React Native, and Web environments.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Platform types
|
|
10
|
+
*/
|
|
11
|
+
export type Platform = 'node' | 'react-native' | 'web';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Platform detection utilities
|
|
15
|
+
*/
|
|
16
|
+
export const PlatformDetector = {
|
|
17
|
+
/**
|
|
18
|
+
* Detect current platform
|
|
19
|
+
*/
|
|
20
|
+
detect(): Platform {
|
|
21
|
+
if (typeof window === 'undefined' && typeof process !== 'undefined' && process.versions?.node) {
|
|
22
|
+
return 'node';
|
|
23
|
+
}
|
|
24
|
+
if (typeof navigator !== 'undefined' && navigator.product === 'ReactNative') {
|
|
25
|
+
return 'react-native';
|
|
26
|
+
}
|
|
27
|
+
return 'web';
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Check if running in Node.js
|
|
32
|
+
*/
|
|
33
|
+
isNode(): boolean {
|
|
34
|
+
return this.detect() === 'node';
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Check if running in React Native
|
|
39
|
+
*/
|
|
40
|
+
isReactNative(): boolean {
|
|
41
|
+
return this.detect() === 'react-native';
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Check if running in Web browser
|
|
46
|
+
*/
|
|
47
|
+
isWeb(): boolean {
|
|
48
|
+
return this.detect() === 'web';
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Crypto adapter interface
|
|
54
|
+
* Platform-specific implementations must implement this interface
|
|
55
|
+
*/
|
|
56
|
+
export interface CryptoAdapter {
|
|
57
|
+
/**
|
|
58
|
+
* Generate random bytes
|
|
59
|
+
*/
|
|
60
|
+
randomBytes(size: number): Promise<Uint8Array>;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Compute SHA-256 hash
|
|
64
|
+
*/
|
|
65
|
+
sha256(message: string): Promise<string>;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Synchronous SHA-256 hash (Node.js only)
|
|
69
|
+
*/
|
|
70
|
+
sha256Sync?(message: string): string;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Get the appropriate crypto adapter for the current platform
|
|
75
|
+
*/
|
|
76
|
+
export async function getCryptoAdapter(): Promise<CryptoAdapter> {
|
|
77
|
+
const platform = PlatformDetector.detect();
|
|
78
|
+
|
|
79
|
+
if (platform === 'node') {
|
|
80
|
+
// eslint-disable-next-line @typescript-eslint/no-implied-eval
|
|
81
|
+
const getCrypto = new Function('return require("crypto")');
|
|
82
|
+
const crypto = getCrypto();
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
async randomBytes(size: number): Promise<Uint8Array> {
|
|
86
|
+
return new Uint8Array(crypto.randomBytes(size));
|
|
87
|
+
},
|
|
88
|
+
async sha256(message: string): Promise<string> {
|
|
89
|
+
return crypto.createHash('sha256').update(message).digest('hex');
|
|
90
|
+
},
|
|
91
|
+
sha256Sync(message: string): string {
|
|
92
|
+
return crypto.createHash('sha256').update(message).digest('hex');
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (platform === 'react-native') {
|
|
98
|
+
// Lazy load expo-crypto (peer dependency)
|
|
99
|
+
try {
|
|
100
|
+
// @ts-ignore - expo-crypto is an optional peer dependency
|
|
101
|
+
const ExpoCrypto = await import('expo-crypto');
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
async randomBytes(size: number): Promise<Uint8Array> {
|
|
105
|
+
const bytes = await ExpoCrypto.getRandomBytesAsync(size);
|
|
106
|
+
return new Uint8Array(bytes);
|
|
107
|
+
},
|
|
108
|
+
async sha256(message: string): Promise<string> {
|
|
109
|
+
return ExpoCrypto.digestStringAsync(
|
|
110
|
+
ExpoCrypto.CryptoDigestAlgorithm.SHA256,
|
|
111
|
+
message
|
|
112
|
+
);
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
} catch (error) {
|
|
116
|
+
throw new Error(
|
|
117
|
+
`expo-crypto is required in React Native environment: ${error instanceof Error ? error.message : String(error)}`
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Web platform - use Web Crypto API
|
|
123
|
+
if (typeof window !== 'undefined' && window.crypto) {
|
|
124
|
+
return {
|
|
125
|
+
async randomBytes(size: number): Promise<Uint8Array> {
|
|
126
|
+
return window.crypto.getRandomValues(new Uint8Array(size));
|
|
127
|
+
},
|
|
128
|
+
async sha256(message: string): Promise<string> {
|
|
129
|
+
const encoder = new TextEncoder();
|
|
130
|
+
const data = encoder.encode(message);
|
|
131
|
+
const hashBuffer = await window.crypto.subtle.digest('SHA-256', data);
|
|
132
|
+
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
133
|
+
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
throw new Error('No suitable crypto implementation found for current platform');
|
|
139
|
+
}
|
|
140
|
+
|