@oxyhq/services 5.16.34 → 5.16.36
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 +8 -26
- 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/crypto/README.md +142 -0
- package/lib/commonjs/crypto/core.js +147 -0
- package/lib/commonjs/crypto/core.js.map +1 -0
- package/lib/commonjs/crypto/index.js +16 -0
- package/lib/commonjs/crypto/index.js.map +1 -1
- package/lib/commonjs/crypto/keyManager.js +19 -24
- package/lib/commonjs/crypto/keyManager.js.map +1 -1
- package/lib/commonjs/crypto/signatureService.js +116 -37
- package/lib/commonjs/crypto/signatureService.js.map +1 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/models/interfaces.js +10 -11
- package/lib/commonjs/models/interfaces.js.map +1 -1
- package/lib/commonjs/node/index.js +10 -1
- package/lib/commonjs/node/index.js.map +1 -1
- package/lib/commonjs/node/signatureService.js +107 -0
- package/lib/commonjs/node/signatureService.js.map +1 -0
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- 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/useLanguageManagement.js.map +1 -1
- package/lib/commonjs/ui/hooks/useSessionManagement.js.map +1 -1
- package/lib/commonjs/ui/index.js +0 -2
- 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 +13 -9
- package/lib/commonjs/ui/screens/OxyAuthScreen.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/crypto/README.md +142 -0
- package/lib/module/crypto/core.js +133 -0
- package/lib/module/crypto/core.js.map +1 -0
- package/lib/module/crypto/index.js +3 -9
- package/lib/module/crypto/index.js.map +1 -1
- package/lib/module/crypto/keyManager.js +19 -24
- package/lib/module/crypto/keyManager.js.map +1 -1
- package/lib/module/crypto/signatureService.js +113 -33
- package/lib/module/crypto/signatureService.js.map +1 -1
- package/lib/module/index.js +0 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/models/interfaces.js +10 -11
- package/lib/module/models/interfaces.js.map +1 -1
- package/lib/module/node/index.js +3 -0
- package/lib/module/node/index.js.map +1 -1
- package/lib/module/node/signatureService.js +101 -0
- package/lib/module/node/signatureService.js.map +1 -0
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- 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/useLanguageManagement.js.map +1 -1
- package/lib/module/ui/hooks/useSessionManagement.js.map +1 -1
- package/lib/module/ui/index.js +0 -1
- 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 +9 -7
- package/lib/module/ui/screens/OxyAuthScreen.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 +1 -2
- 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/SessionService.d.ts +1 -1
- package/lib/typescript/core/services/SessionService.d.ts.map +1 -1
- package/lib/typescript/crypto/core.d.ts +56 -0
- package/lib/typescript/crypto/core.d.ts.map +1 -0
- package/lib/typescript/crypto/index.d.ts +1 -9
- package/lib/typescript/crypto/index.d.ts.map +1 -1
- package/lib/typescript/crypto/keyManager.d.ts +13 -1
- package/lib/typescript/crypto/keyManager.d.ts.map +1 -1
- package/lib/typescript/crypto/signatureService.d.ts +15 -9
- package/lib/typescript/crypto/signatureService.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +1 -1
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/models/interfaces.d.ts +68 -15
- package/lib/typescript/models/interfaces.d.ts.map +1 -1
- package/lib/typescript/node/index.d.ts +1 -0
- package/lib/typescript/node/index.d.ts.map +1 -1
- package/lib/typescript/node/signatureService.d.ts +55 -0
- package/lib/typescript/node/signatureService.d.ts.map +1 -0
- package/lib/typescript/ui/context/OxyContext.d.ts +1 -2
- package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/ui/context/hooks/useAuthOperations.d.ts +1 -2
- package/lib/typescript/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
- package/lib/typescript/ui/context/hooks/useLanguageManagement.d.ts +1 -2
- 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/useLanguageManagement.d.ts +1 -2
- package/lib/typescript/ui/hooks/useLanguageManagement.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/useSessionManagement.d.ts +1 -2
- 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/package.json +6 -2
- package/src/core/OxyServices.base.ts +1 -2
- package/src/core/mixins/OxyServices.auth.ts +1 -1
- package/src/core/mixins/OxyServices.user.ts +1 -2
- package/src/core/mixins/OxyServices.utility.ts +1 -2
- package/src/core/services/SessionService.ts +1 -1
- package/src/crypto/README.md +142 -0
- package/src/crypto/__tests__/core.test.ts +203 -0
- package/src/crypto/core.ts +142 -0
- package/src/crypto/index.ts +3 -10
- package/src/crypto/keyManager.ts +25 -21
- package/src/crypto/signatureService.ts +137 -37
- package/src/index.ts +2 -1
- package/src/models/interfaces.ts +73 -21
- package/src/node/index.ts +3 -0
- package/src/node/signatureService.ts +126 -0
- package/src/ui/context/OxyContext.tsx +1 -2
- package/src/ui/context/hooks/useAuthOperations.ts +1 -2
- package/src/ui/context/hooks/useLanguageManagement.ts +1 -2
- package/src/ui/hooks/auth/index.ts +2 -0
- 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/useLanguageManagement.ts +1 -2
- package/src/ui/hooks/useSessionManagement.ts +1 -2
- package/src/ui/index.ts +1 -2
- package/src/ui/screens/AccountSettingsScreen.tsx +6 -6
- package/src/ui/screens/AccountSwitcherScreen.tsx +1 -1
- package/src/ui/screens/OxyAuthScreen.tsx +9 -5
- 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/lib/commonjs/core/services/AuthService.js +0 -156
- package/lib/commonjs/core/services/AuthService.js.map +0 -1
- package/lib/commonjs/core/services/SessionTransportService.js +0 -64
- package/lib/commonjs/core/services/SessionTransportService.js.map +0 -1
- package/lib/commonjs/core/services/UserService.js +0 -123
- package/lib/commonjs/core/services/UserService.js.map +0 -1
- package/lib/commonjs/core/services/index.js +0 -34
- package/lib/commonjs/core/services/index.js.map +0 -1
- package/lib/module/core/services/AuthService.js +0 -151
- package/lib/module/core/services/AuthService.js.map +0 -1
- package/lib/module/core/services/SessionTransportService.js +0 -59
- package/lib/module/core/services/SessionTransportService.js.map +0 -1
- package/lib/module/core/services/UserService.js +0 -118
- package/lib/module/core/services/UserService.js.map +0 -1
- package/lib/module/core/services/index.js +0 -16
- package/lib/module/core/services/index.js.map +0 -1
- package/lib/typescript/core/services/AuthService.d.ts +0 -50
- package/lib/typescript/core/services/AuthService.d.ts.map +0 -1
- package/lib/typescript/core/services/SessionTransportService.d.ts +0 -31
- package/lib/typescript/core/services/SessionTransportService.d.ts.map +0 -1
- package/lib/typescript/core/services/UserService.d.ts +0 -39
- package/lib/typescript/core/services/UserService.d.ts.map +0 -1
- package/lib/typescript/core/services/index.d.ts +0 -13
- package/lib/typescript/core/services/index.d.ts.map +0 -1
- package/src/core/services/AuthService.ts +0 -153
- package/src/core/services/SessionTransportService.ts +0 -69
- package/src/core/services/UserService.ts +0 -125
- package/src/core/services/index.ts +0 -14
|
@@ -4,24 +4,88 @@
|
|
|
4
4
|
* Handles signing and verification of messages using ECDSA secp256k1.
|
|
5
5
|
* Used for authenticating requests and proving identity ownership.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
* For
|
|
7
|
+
* This service provides async methods for cross-platform compatibility (React Native + Node).
|
|
8
|
+
* For Node.js-only synchronous operations, use the node/signatureService module.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import { ec as EC } from 'elliptic';
|
|
12
11
|
import { KeyManager } from './keyManager';
|
|
13
12
|
import {
|
|
13
|
+
verifySignatureCore,
|
|
14
|
+
isValidPublicKey as validatePublicKey,
|
|
15
|
+
isTimestampFresh,
|
|
14
16
|
buildAuthMessage,
|
|
15
17
|
buildRegistrationMessage,
|
|
16
18
|
buildRequestMessage,
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
} from '
|
|
19
|
+
shortenPublicKey as shortenKey,
|
|
20
|
+
getEllipticCurve,
|
|
21
|
+
} from './core';
|
|
20
22
|
|
|
21
|
-
|
|
23
|
+
// Lazy import for expo-crypto
|
|
24
|
+
let ExpoCrypto: typeof import('expo-crypto') | null = null;
|
|
22
25
|
|
|
23
|
-
|
|
24
|
-
|
|
26
|
+
const ec = getEllipticCurve();
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Check if we're in a React Native environment
|
|
30
|
+
*/
|
|
31
|
+
function isReactNative(): boolean {
|
|
32
|
+
return typeof navigator !== 'undefined' && navigator.product === 'ReactNative';
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Check if we're in a Node.js environment
|
|
37
|
+
*/
|
|
38
|
+
function isNodeJS(): boolean {
|
|
39
|
+
return typeof process !== 'undefined' && process.versions != null && process.versions.node != null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Initialize expo-crypto module
|
|
44
|
+
*/
|
|
45
|
+
async function initExpoCrypto(): Promise<typeof import('expo-crypto')> {
|
|
46
|
+
if (!ExpoCrypto) {
|
|
47
|
+
ExpoCrypto = await import('expo-crypto');
|
|
48
|
+
}
|
|
49
|
+
return ExpoCrypto;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Compute SHA-256 hash of a string
|
|
54
|
+
*/
|
|
55
|
+
async function sha256(message: string): Promise<string> {
|
|
56
|
+
// In React Native, always use expo-crypto
|
|
57
|
+
if (isReactNative() || !isNodeJS()) {
|
|
58
|
+
const Crypto = await initExpoCrypto();
|
|
59
|
+
return Crypto.digestStringAsync(
|
|
60
|
+
Crypto.CryptoDigestAlgorithm.SHA256,
|
|
61
|
+
message
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// In Node.js, use Node's crypto module
|
|
66
|
+
// Use Function constructor to prevent Metro bundler from statically analyzing this require
|
|
67
|
+
// This ensures the require is only evaluated in Node.js runtime, not during Metro bundling
|
|
68
|
+
try {
|
|
69
|
+
// eslint-disable-next-line @typescript-eslint/no-implied-eval
|
|
70
|
+
const getCrypto = new Function('return require("crypto")');
|
|
71
|
+
const crypto = getCrypto();
|
|
72
|
+
return crypto.createHash('sha256').update(message).digest('hex');
|
|
73
|
+
} catch (error) {
|
|
74
|
+
// Fallback to expo-crypto if Node crypto fails
|
|
75
|
+
const Crypto = await initExpoCrypto();
|
|
76
|
+
return Crypto.digestStringAsync(
|
|
77
|
+
Crypto.CryptoDigestAlgorithm.SHA256,
|
|
78
|
+
message
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface SignedMessage {
|
|
84
|
+
message: string;
|
|
85
|
+
signature: string;
|
|
86
|
+
publicKey: string;
|
|
87
|
+
timestamp: number;
|
|
88
|
+
}
|
|
25
89
|
|
|
26
90
|
export interface AuthChallenge {
|
|
27
91
|
challenge: string;
|
|
@@ -32,23 +96,39 @@ export interface AuthChallenge {
|
|
|
32
96
|
export class SignatureService {
|
|
33
97
|
/**
|
|
34
98
|
* Generate a random challenge string (for offline use)
|
|
35
|
-
* Uses
|
|
99
|
+
* Uses expo-crypto in React Native, crypto.randomBytes in Node.js
|
|
36
100
|
*/
|
|
37
101
|
static async generateChallenge(): Promise<string> {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
.
|
|
102
|
+
if (isReactNative() || !isNodeJS()) {
|
|
103
|
+
// Use expo-crypto for React Native (expo-random is deprecated)
|
|
104
|
+
const Crypto = await initExpoCrypto();
|
|
105
|
+
const randomBytes = await Crypto.getRandomBytesAsync(32);
|
|
106
|
+
return Array.from(randomBytes)
|
|
107
|
+
.map((b: number) => b.toString(16).padStart(2, '0'))
|
|
108
|
+
.join('');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Node.js fallback
|
|
112
|
+
try {
|
|
113
|
+
// eslint-disable-next-line @typescript-eslint/no-implied-eval
|
|
114
|
+
const getCrypto = new Function('return require("crypto")');
|
|
115
|
+
const crypto = getCrypto();
|
|
116
|
+
return crypto.randomBytes(32).toString('hex');
|
|
117
|
+
} catch (error) {
|
|
118
|
+
// Fallback to expo-crypto if Node crypto fails
|
|
119
|
+
const Crypto = await initExpoCrypto();
|
|
120
|
+
const randomBytes = await Crypto.getRandomBytesAsync(32);
|
|
121
|
+
return Array.from(randomBytes)
|
|
122
|
+
.map((b: number) => b.toString(16).padStart(2, '0'))
|
|
123
|
+
.join('');
|
|
124
|
+
}
|
|
43
125
|
}
|
|
44
126
|
|
|
45
127
|
/**
|
|
46
128
|
* Hash a message using SHA-256
|
|
47
|
-
* Uses shared crypto adapter
|
|
48
129
|
*/
|
|
49
130
|
static async hashMessage(message: string): Promise<string> {
|
|
50
|
-
|
|
51
|
-
return adapter.sha256(message);
|
|
131
|
+
return sha256(message);
|
|
52
132
|
}
|
|
53
133
|
|
|
54
134
|
/**
|
|
@@ -61,8 +141,7 @@ export class SignatureService {
|
|
|
61
141
|
throw new Error('No identity found. Please create or import an identity first.');
|
|
62
142
|
}
|
|
63
143
|
|
|
64
|
-
const
|
|
65
|
-
const messageHash = await adapter.sha256(message);
|
|
144
|
+
const messageHash = await sha256(message);
|
|
66
145
|
const signature = keyPair.sign(messageHash);
|
|
67
146
|
return signature.toDER('hex');
|
|
68
147
|
}
|
|
@@ -73,19 +152,43 @@ export class SignatureService {
|
|
|
73
152
|
*/
|
|
74
153
|
static async signWithKey(message: string, privateKey: string): Promise<string> {
|
|
75
154
|
const keyPair = ec.keyFromPrivate(privateKey);
|
|
76
|
-
const
|
|
77
|
-
const messageHash = await adapter.sha256(message);
|
|
155
|
+
const messageHash = await sha256(message);
|
|
78
156
|
const signature = keyPair.sign(messageHash);
|
|
79
157
|
return signature.toDER('hex');
|
|
80
158
|
}
|
|
81
159
|
|
|
82
160
|
/**
|
|
83
161
|
* Verify a signature against a message and public key
|
|
84
|
-
* Uses shared SignatureService for verification
|
|
85
162
|
*/
|
|
86
163
|
static async verify(message: string, signature: string, publicKey: string): Promise<boolean> {
|
|
87
|
-
|
|
88
|
-
|
|
164
|
+
try {
|
|
165
|
+
const messageHash = await sha256(message);
|
|
166
|
+
return verifySignatureCore(messageHash, signature, publicKey);
|
|
167
|
+
} catch {
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Synchronous verification (for Node.js backend)
|
|
174
|
+
* Uses crypto module directly for hashing
|
|
175
|
+
* Note: This method should only be used in Node.js environments
|
|
176
|
+
*/
|
|
177
|
+
static verifySync(message: string, signature: string, publicKey: string): boolean {
|
|
178
|
+
try {
|
|
179
|
+
if (!isNodeJS()) {
|
|
180
|
+
// In React Native, use async verify instead
|
|
181
|
+
throw new Error('verifySync should only be used in Node.js. Use verify() in React Native.');
|
|
182
|
+
}
|
|
183
|
+
// Use Function constructor to prevent Metro bundler from statically analyzing this require
|
|
184
|
+
// eslint-disable-next-line @typescript-eslint/no-implied-eval
|
|
185
|
+
const getCrypto = new Function('return require("crypto")');
|
|
186
|
+
const crypto = getCrypto();
|
|
187
|
+
const messageHash = crypto.createHash('sha256').update(message).digest('hex');
|
|
188
|
+
return verifySignatureCore(messageHash, signature, publicKey);
|
|
189
|
+
} catch {
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
89
192
|
}
|
|
90
193
|
|
|
91
194
|
/**
|
|
@@ -112,13 +215,11 @@ export class SignatureService {
|
|
|
112
215
|
/**
|
|
113
216
|
* Verify a signed message object
|
|
114
217
|
* Checks both signature validity and timestamp freshness
|
|
115
|
-
* Uses shared SignatureService for verification
|
|
116
218
|
*/
|
|
117
219
|
static async verifySignedMessage(
|
|
118
220
|
signedMessage: SignedMessage,
|
|
119
221
|
maxAgeMs: number = 5 * 60 * 1000 // 5 minutes default
|
|
120
222
|
): Promise<boolean> {
|
|
121
|
-
const { SignatureService: SharedSignatureService, isTimestampFresh } = await import('@oxyhq/shared');
|
|
122
223
|
const { message, signature, publicKey, timestamp } = signedMessage;
|
|
123
224
|
|
|
124
225
|
// Check timestamp freshness
|
|
@@ -128,7 +229,7 @@ export class SignatureService {
|
|
|
128
229
|
|
|
129
230
|
// Verify signature
|
|
130
231
|
const messageWithTimestamp = `${message}:${timestamp}`;
|
|
131
|
-
return
|
|
232
|
+
return SignatureService.verify(messageWithTimestamp, signature, publicKey);
|
|
132
233
|
}
|
|
133
234
|
|
|
134
235
|
/**
|
|
@@ -154,22 +255,21 @@ export class SignatureService {
|
|
|
154
255
|
|
|
155
256
|
/**
|
|
156
257
|
* Verify a challenge response
|
|
157
|
-
* Uses shared SignatureService for verification
|
|
158
258
|
*/
|
|
159
259
|
static async verifyChallengeResponse(
|
|
160
260
|
originalChallenge: string,
|
|
161
261
|
response: AuthChallenge,
|
|
162
262
|
maxAgeMs: number = 5 * 60 * 1000
|
|
163
263
|
): Promise<boolean> {
|
|
164
|
-
const { SignatureService: SharedSignatureService } = await import('@oxyhq/shared');
|
|
165
264
|
const { challenge: signature, publicKey, timestamp } = response;
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
);
|
|
265
|
+
|
|
266
|
+
// Check timestamp freshness
|
|
267
|
+
if (!isTimestampFresh(timestamp, maxAgeMs)) {
|
|
268
|
+
return false;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const message = buildAuthMessage(publicKey, originalChallenge, timestamp);
|
|
272
|
+
return SignatureService.verify(message, signature, publicKey);
|
|
173
273
|
}
|
|
174
274
|
|
|
175
275
|
/**
|
package/src/index.ts
CHANGED
|
@@ -49,9 +49,10 @@ export {
|
|
|
49
49
|
export type { LanguageMetadata } from './utils/languageUtils';
|
|
50
50
|
|
|
51
51
|
// Type exports
|
|
52
|
-
// Note: User and LoginResponse should be imported from @oxyhq/shared
|
|
53
52
|
export type {
|
|
54
53
|
OxyConfig,
|
|
54
|
+
User,
|
|
55
|
+
LoginResponse,
|
|
55
56
|
Notification,
|
|
56
57
|
Wallet,
|
|
57
58
|
Transaction,
|
package/src/models/interfaces.ts
CHANGED
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Services Package Interfaces
|
|
3
|
-
*
|
|
4
|
-
* Package-specific interfaces. For shared models (User, Session, etc.),
|
|
5
|
-
* import directly from @oxyhq/shared:
|
|
6
|
-
*
|
|
7
|
-
* import { User, LoginResponse, Session } from '@oxyhq/shared';
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
1
|
export interface OxyConfig {
|
|
11
2
|
baseURL: string;
|
|
12
3
|
cloudURL?: string;
|
|
@@ -30,8 +21,65 @@ export interface OxyConfig {
|
|
|
30
21
|
onRequestError?: (url: string, method: string, error: Error) => void;
|
|
31
22
|
}
|
|
32
23
|
|
|
33
|
-
|
|
34
|
-
|
|
24
|
+
/**
|
|
25
|
+
* User Model
|
|
26
|
+
*
|
|
27
|
+
* IMPORTANT:
|
|
28
|
+
* - id: MongoDB ObjectId (24 hex characters) - PRIMARY IDENTIFIER for all internal operations
|
|
29
|
+
* - publicKey: Cryptographic public key (130 hex characters) - LOOKUP KEY for authentication and identity operations
|
|
30
|
+
*
|
|
31
|
+
* Never use publicKey as an ID. Always use id (ObjectId) for:
|
|
32
|
+
* - Database queries
|
|
33
|
+
* - Session userId
|
|
34
|
+
* - Token userId
|
|
35
|
+
* - Socket room names
|
|
36
|
+
* - API route parameters (unless explicitly doing publicKey lookup)
|
|
37
|
+
*/
|
|
38
|
+
export interface User {
|
|
39
|
+
id: string; // MongoDB ObjectId - PRIMARY IDENTIFIER (always 24 hex chars)
|
|
40
|
+
publicKey: string; // Cryptographic public key - LOOKUP KEY (130 hex chars for secp256k1)
|
|
41
|
+
username: string;
|
|
42
|
+
email?: string;
|
|
43
|
+
// Avatar file id (asset id)
|
|
44
|
+
avatar?: string;
|
|
45
|
+
// Privacy and security settings
|
|
46
|
+
privacySettings?: {
|
|
47
|
+
[key: string]: unknown;
|
|
48
|
+
};
|
|
49
|
+
name?: {
|
|
50
|
+
first?: string;
|
|
51
|
+
last?: string;
|
|
52
|
+
full?: string; // virtual, not stored in DB, returned by API
|
|
53
|
+
[key: string]: unknown;
|
|
54
|
+
};
|
|
55
|
+
bio?: string;
|
|
56
|
+
karma?: number;
|
|
57
|
+
location?: string;
|
|
58
|
+
website?: string;
|
|
59
|
+
createdAt?: string;
|
|
60
|
+
updatedAt?: string;
|
|
61
|
+
links?: Array<{
|
|
62
|
+
title?: string;
|
|
63
|
+
description?: string;
|
|
64
|
+
image?: string;
|
|
65
|
+
link: string;
|
|
66
|
+
}>;
|
|
67
|
+
// Social counts
|
|
68
|
+
_count?: {
|
|
69
|
+
followers?: number;
|
|
70
|
+
following?: number;
|
|
71
|
+
};
|
|
72
|
+
accountExpiresAfterInactivityDays?: number | null; // Days of inactivity before account expires (null = never expire)
|
|
73
|
+
[key: string]: unknown;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface LoginResponse {
|
|
77
|
+
accessToken?: string;
|
|
78
|
+
refreshToken?: string;
|
|
79
|
+
token?: string; // For backwards compatibility
|
|
80
|
+
user: User;
|
|
81
|
+
message?: string;
|
|
82
|
+
}
|
|
35
83
|
|
|
36
84
|
export interface Notification {
|
|
37
85
|
id: string;
|
|
@@ -104,8 +152,17 @@ export interface TransactionResponse {
|
|
|
104
152
|
transaction: Transaction;
|
|
105
153
|
}
|
|
106
154
|
|
|
107
|
-
|
|
108
|
-
|
|
155
|
+
export interface PaginationInfo {
|
|
156
|
+
total: number;
|
|
157
|
+
limit: number;
|
|
158
|
+
offset: number;
|
|
159
|
+
hasMore: boolean;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export interface SearchProfilesResponse {
|
|
163
|
+
data: User[];
|
|
164
|
+
pagination: PaginationInfo;
|
|
165
|
+
}
|
|
109
166
|
|
|
110
167
|
export interface KarmaRule {
|
|
111
168
|
id: string;
|
|
@@ -318,6 +375,8 @@ export interface AssetUrlResponse {
|
|
|
318
375
|
|
|
319
376
|
export interface AssetDeleteSummary {
|
|
320
377
|
fileId: string;
|
|
378
|
+
wouldDelete: boolean;
|
|
379
|
+
affectedApps: string[];
|
|
321
380
|
remainingLinks: number;
|
|
322
381
|
variants: string[];
|
|
323
382
|
}
|
|
@@ -435,7 +494,6 @@ export interface AssetUploadProgress {
|
|
|
435
494
|
}
|
|
436
495
|
|
|
437
496
|
// Device Session interfaces
|
|
438
|
-
// Note: User type should be imported from @oxyhq/shared
|
|
439
497
|
export interface DeviceSession {
|
|
440
498
|
sessionId: string;
|
|
441
499
|
deviceId: string;
|
|
@@ -444,13 +502,7 @@ export interface DeviceSession {
|
|
|
444
502
|
lastActive: string;
|
|
445
503
|
expiresAt: string;
|
|
446
504
|
isCurrent: boolean;
|
|
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 @oxyhq/shared if needed
|
|
505
|
+
user?: User;
|
|
454
506
|
createdAt?: string;
|
|
455
507
|
}
|
|
456
508
|
|
package/src/node/index.ts
CHANGED
|
@@ -13,5 +13,8 @@ export { OxyServices, OXY_CLOUD_URL, oxyClient };
|
|
|
13
13
|
export { Models }; // Export all models as a namespace
|
|
14
14
|
export * from '../models/interfaces'; // Export all models directly
|
|
15
15
|
|
|
16
|
+
// ------------- Node-Specific Crypto Exports -------------
|
|
17
|
+
export { SignatureService } from './signatureService';
|
|
18
|
+
|
|
16
19
|
// Default export for consistency or specific use cases if needed
|
|
17
20
|
export default OxyServices;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node.js Signature Service
|
|
3
|
+
*
|
|
4
|
+
* Provides synchronous signature operations for Node.js backend.
|
|
5
|
+
* Uses Node's crypto module for hashing and the shared core for verification.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import crypto from 'crypto';
|
|
9
|
+
import {
|
|
10
|
+
verifySignatureCore,
|
|
11
|
+
isValidPublicKey,
|
|
12
|
+
isTimestampFresh,
|
|
13
|
+
buildAuthMessage,
|
|
14
|
+
buildRegistrationMessage,
|
|
15
|
+
buildRequestMessage,
|
|
16
|
+
shortenPublicKey,
|
|
17
|
+
CHALLENGE_TTL_MS,
|
|
18
|
+
MAX_SIGNATURE_AGE_MS,
|
|
19
|
+
} from '../crypto/core';
|
|
20
|
+
|
|
21
|
+
export class SignatureService {
|
|
22
|
+
/**
|
|
23
|
+
* Generate a random challenge string
|
|
24
|
+
*/
|
|
25
|
+
static generateChallenge(): string {
|
|
26
|
+
return crypto.randomBytes(32).toString('hex');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Compute SHA-256 hash of a message (synchronous)
|
|
31
|
+
*/
|
|
32
|
+
static hashMessage(message: string): string {
|
|
33
|
+
return crypto.createHash('sha256').update(message).digest('hex');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Verify an ECDSA signature (synchronous)
|
|
38
|
+
*
|
|
39
|
+
* @param message - The original message that was signed
|
|
40
|
+
* @param signature - The signature in DER format (hex encoded)
|
|
41
|
+
* @param publicKey - The public key (hex encoded, uncompressed)
|
|
42
|
+
* @returns true if the signature is valid
|
|
43
|
+
*/
|
|
44
|
+
static verifySignature(message: string, signature: string, publicKey: string): boolean {
|
|
45
|
+
const messageHash = SignatureService.hashMessage(message);
|
|
46
|
+
return verifySignatureCore(messageHash, signature, publicKey);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Verify an authentication challenge response
|
|
51
|
+
*
|
|
52
|
+
* @param publicKey - The user's public key
|
|
53
|
+
* @param challenge - The original challenge string
|
|
54
|
+
* @param signature - The signature of the auth message
|
|
55
|
+
* @param timestamp - The timestamp when the signature was created
|
|
56
|
+
* @returns true if the challenge response is valid
|
|
57
|
+
*/
|
|
58
|
+
static verifyChallengeResponse(
|
|
59
|
+
publicKey: string,
|
|
60
|
+
challenge: string,
|
|
61
|
+
signature: string,
|
|
62
|
+
timestamp: number
|
|
63
|
+
): boolean {
|
|
64
|
+
// Check timestamp is not too old
|
|
65
|
+
if (!isTimestampFresh(timestamp, CHALLENGE_TTL_MS)) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Build the message and verify signature
|
|
70
|
+
const message = buildAuthMessage(publicKey, challenge, timestamp);
|
|
71
|
+
return SignatureService.verifySignature(message, signature, publicKey);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Verify a registration signature
|
|
76
|
+
* Signature format: oxy:register:{publicKey}:{timestamp}
|
|
77
|
+
*/
|
|
78
|
+
static verifyRegistrationSignature(
|
|
79
|
+
publicKey: string,
|
|
80
|
+
signature: string,
|
|
81
|
+
timestamp: number
|
|
82
|
+
): boolean {
|
|
83
|
+
// Check timestamp freshness
|
|
84
|
+
if (!isTimestampFresh(timestamp, MAX_SIGNATURE_AGE_MS)) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const message = buildRegistrationMessage(publicKey, timestamp);
|
|
89
|
+
return SignatureService.verifySignature(message, signature, publicKey);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Verify a signed request
|
|
94
|
+
* Used for authenticated API operations
|
|
95
|
+
*/
|
|
96
|
+
static verifyRequestSignature(
|
|
97
|
+
publicKey: string,
|
|
98
|
+
data: Record<string, unknown>,
|
|
99
|
+
signature: string,
|
|
100
|
+
timestamp: number
|
|
101
|
+
): boolean {
|
|
102
|
+
// Check timestamp freshness
|
|
103
|
+
if (!isTimestampFresh(timestamp, MAX_SIGNATURE_AGE_MS)) {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const message = buildRequestMessage(publicKey, timestamp, data);
|
|
108
|
+
return SignatureService.verifySignature(message, signature, publicKey);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Validate that a string is a valid public key
|
|
113
|
+
*/
|
|
114
|
+
static isValidPublicKey(publicKey: string): boolean {
|
|
115
|
+
return isValidPublicKey(publicKey);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Get a shortened display version of a public key
|
|
120
|
+
*/
|
|
121
|
+
static shortenPublicKey(publicKey: string): string {
|
|
122
|
+
return shortenPublicKey(publicKey);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export default SignatureService;
|
|
@@ -11,8 +11,7 @@ import {
|
|
|
11
11
|
} from 'react';
|
|
12
12
|
import { Platform } from 'react-native';
|
|
13
13
|
import { OxyServices } from '../../core';
|
|
14
|
-
import type { ApiError } from '../../models/interfaces';
|
|
15
|
-
import type { User } from '@oxyhq/shared';
|
|
14
|
+
import type { User, ApiError } from '../../models/interfaces';
|
|
16
15
|
import type { ClientSession } from '../../models/session';
|
|
17
16
|
import { toast } from '../../lib/sonner';
|
|
18
17
|
import { useAuthStore, type AuthState } from '../stores/authStore';
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { useCallback } from 'react';
|
|
2
|
-
import type { ApiError } from '../../../models/interfaces';
|
|
3
|
-
import type { User } from '@oxyhq/shared';
|
|
2
|
+
import type { ApiError, User } from '../../../models/interfaces';
|
|
4
3
|
import type { AuthState } from '../../stores/authStore';
|
|
5
4
|
import type { ClientSession, SessionLoginResponse } from '../../../models/session';
|
|
6
5
|
import { DeviceManager } from '../../../utils/deviceManager';
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
2
|
-
import type { ApiError } from '../../../models/interfaces';
|
|
3
|
-
import type { User } from '@oxyhq/shared';
|
|
2
|
+
import type { ApiError, User } from '../../../models/interfaces';
|
|
4
3
|
import {
|
|
5
4
|
getLanguageMetadata,
|
|
6
5
|
getLanguageName,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|
2
|
-
import type { User } from '
|
|
2
|
+
import type { User } from '../../../models/interfaces';
|
|
3
3
|
import { queryKeys, invalidateAccountQueries, invalidateUserQueries } from '../queries/queryKeys';
|
|
4
4
|
import { useOxy } from '../../context/OxyContext';
|
|
5
5
|
import { toast } from '../../../lib/sonner';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|
2
|
-
import type { User } from '
|
|
2
|
+
import type { User } from '../../../models/interfaces';
|
|
3
3
|
import { queryKeys, invalidateSessionQueries } from '../queries/queryKeys';
|
|
4
4
|
import { useOxy } from '../../context/OxyContext';
|
|
5
5
|
import { toast } from '../../../lib/sonner';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useQuery, useQueries } from '@tanstack/react-query';
|
|
2
|
-
import type { User } from '
|
|
2
|
+
import type { User } from '../../../models/interfaces';
|
|
3
3
|
import type { OxyServices } from '../../../core';
|
|
4
4
|
import { queryKeys } from './queryKeys';
|
|
5
5
|
import { useOxy } from '../../context/OxyContext';
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
2
|
-
import type { ApiError } from '../../models/interfaces';
|
|
3
|
-
import type { User } from '@oxyhq/shared';
|
|
2
|
+
import type { ApiError, User } from '../../models/interfaces';
|
|
4
3
|
import {
|
|
5
4
|
getLanguageMetadata,
|
|
6
5
|
getLanguageName,
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { useCallback, useMemo, useRef, useState } from 'react';
|
|
2
|
-
import type { ApiError } from '../../models/interfaces';
|
|
3
|
-
import type { User } from '@oxyhq/shared';
|
|
2
|
+
import type { ApiError, User } from '../../models/interfaces';
|
|
4
3
|
import type { ClientSession } from '../../models/session';
|
|
5
4
|
import { mergeSessions, normalizeAndSortSessions, sessionsArraysEqual } from '../../utils/sessionUtils';
|
|
6
5
|
import { fetchSessionsWithFallback, mapSessionsToClient, validateSessionBatch } from '../utils/sessionHelpers';
|
package/src/ui/index.ts
CHANGED
|
@@ -81,5 +81,4 @@ export {
|
|
|
81
81
|
|
|
82
82
|
// Re-export core services for convenience in UI context
|
|
83
83
|
export { OxyServices } from '../core';
|
|
84
|
-
|
|
85
|
-
export type { ApiError } from '../models/interfaces';
|
|
84
|
+
export type { User, LoginResponse, ApiError } from '../models/interfaces';
|
|
@@ -233,7 +233,7 @@ const AccountSettingsScreen: React.FC<BaseScreenProps & { initialField?: string;
|
|
|
233
233
|
|
|
234
234
|
// Handle locations - convert single location to array format
|
|
235
235
|
if (finalUser.locations && Array.isArray(finalUser.locations)) {
|
|
236
|
-
setLocations(finalUser.locations.map((loc
|
|
236
|
+
setLocations(finalUser.locations.map((loc, index) => ({
|
|
237
237
|
id: loc.id || `existing-${index}`,
|
|
238
238
|
name: loc.name,
|
|
239
239
|
label: loc.label,
|
|
@@ -252,17 +252,17 @@ const AccountSettingsScreen: React.FC<BaseScreenProps & { initialField?: string;
|
|
|
252
252
|
|
|
253
253
|
// Handle links - simple and direct like other fields
|
|
254
254
|
if (finalUser.linksMetadata && Array.isArray(finalUser.linksMetadata)) {
|
|
255
|
-
const urls = finalUser.linksMetadata.map(
|
|
255
|
+
const urls = finalUser.linksMetadata.map(l => l.url);
|
|
256
256
|
setLinks(urls);
|
|
257
|
-
const metadataWithIds = finalUser.linksMetadata.map((link
|
|
257
|
+
const metadataWithIds = finalUser.linksMetadata.map((link, index) => ({
|
|
258
258
|
...link,
|
|
259
259
|
id: link.id || `existing-${index}`
|
|
260
260
|
}));
|
|
261
261
|
setLinksMetadata(metadataWithIds);
|
|
262
262
|
} else if (Array.isArray(finalUser.links)) {
|
|
263
|
-
const simpleLinks = finalUser.links.map(
|
|
263
|
+
const simpleLinks = finalUser.links.map(l => typeof l === 'string' ? l : l.link).filter(Boolean);
|
|
264
264
|
setLinks(simpleLinks);
|
|
265
|
-
const linksWithMetadata = simpleLinks.map((url
|
|
265
|
+
const linksWithMetadata = simpleLinks.map((url, index) => ({
|
|
266
266
|
url,
|
|
267
267
|
title: url.replace(/^https?:\/\//, '').replace(/\/$/, ''),
|
|
268
268
|
description: `Link to ${url}`,
|
|
@@ -553,7 +553,7 @@ const AccountSettingsScreen: React.FC<BaseScreenProps & { initialField?: string;
|
|
|
553
553
|
}
|
|
554
554
|
|
|
555
555
|
if (currentUser.linksMetadata && Array.isArray(currentUser.linksMetadata)) {
|
|
556
|
-
setLinksMetadata(currentUser.linksMetadata.map((link
|
|
556
|
+
setLinksMetadata(currentUser.linksMetadata.map((link, index) => ({
|
|
557
557
|
...link,
|
|
558
558
|
id: link.id || `existing-${index}`
|
|
559
559
|
})));
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
import type { BaseScreenProps } from '../types/navigation';
|
|
16
16
|
import type { ClientSession } from '../../models/session';
|
|
17
17
|
import { fontFamilies } from '../styles/fonts';
|
|
18
|
-
import type { User } from '
|
|
18
|
+
import type { User } from '../../models/interfaces';
|
|
19
19
|
import { toast } from '../../lib/sonner';
|
|
20
20
|
import { confirmAction } from '../utils/confirmAction';
|
|
21
21
|
import OxyIcon from '../components/icon/OxyIcon';
|