@oxyhq/services 5.16.34 → 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/lib/commonjs/core/services/SessionTransportService.js +1 -1
- package/lib/commonjs/crypto/keyManager.js +2 -4
- package/lib/commonjs/crypto/keyManager.js.map +1 -1
- package/lib/commonjs/crypto/signatureService.js +8 -17
- 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 +7 -7
- 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/index.js +1 -1
- package/lib/commonjs/ui/screens/OxyAuthScreen.js +6 -11
- package/lib/commonjs/ui/screens/OxyAuthScreen.js.map +1 -1
- package/lib/module/core/services/SessionTransportService.js +1 -1
- package/lib/module/core/services/SessionTransportService.js.map +1 -1
- package/lib/module/crypto/keyManager.js +2 -4
- package/lib/module/crypto/keyManager.js.map +1 -1
- package/lib/module/crypto/signatureService.js +2 -12
- package/lib/module/crypto/signatureService.js.map +1 -1
- package/lib/module/index.js +2 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/models/interfaces.js +7 -7
- 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/index.js +1 -1
- package/lib/module/ui/screens/OxyAuthScreen.js +2 -9
- package/lib/module/ui/screens/OxyAuthScreen.js.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 -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 +1 -1
- package/lib/typescript/core/services/AuthService.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/core/services/SessionTransportService.d.ts +1 -1
- package/lib/typescript/core/services/SessionTransportService.d.ts.map +1 -1
- package/lib/typescript/core/services/UserService.d.ts +1 -1
- package/lib/typescript/core/services/UserService.d.ts.map +1 -1
- package/lib/typescript/crypto/keyManager.d.ts.map +1 -1
- package/lib/typescript/crypto/signatureService.d.ts +3 -3
- package/lib/typescript/crypto/signatureService.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +1 -0
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/models/interfaces.d.ts +2 -2
- 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 +1 -1
- package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/ui/context/hooks/useAuthOperations.d.ts +1 -1
- package/lib/typescript/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
- package/lib/typescript/ui/context/hooks/useLanguageManagement.d.ts +1 -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/useLanguageManagement.d.ts +1 -1
- package/lib/typescript/ui/hooks/useLanguageManagement.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/useSessionManagement.d.ts +1 -1
- package/lib/typescript/ui/hooks/useSessionManagement.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 +1 -2
- package/src/core/OxyServices.base.ts +1 -1
- package/src/core/mixins/OxyServices.auth.ts +1 -1
- package/src/core/mixins/OxyServices.user.ts +1 -1
- package/src/core/mixins/OxyServices.utility.ts +1 -1
- package/src/core/services/AuthService.ts +1 -1
- package/src/core/services/SessionService.ts +1 -1
- package/src/core/services/SessionTransportService.ts +1 -1
- package/src/core/services/UserService.ts +1 -1
- package/src/crypto/keyManager.ts +2 -2
- package/src/crypto/signatureService.ts +5 -6
- package/src/index.ts +3 -1
- package/src/models/interfaces.ts +8 -8
- 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 +1 -1
- package/src/ui/context/hooks/useAuthOperations.ts +1 -1
- package/src/ui/context/hooks/useLanguageManagement.ts +1 -1
- 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 -1
- package/src/ui/hooks/useSessionManagement.ts +1 -1
- package/src/ui/index.ts +1 -1
- package/src/ui/screens/AccountSwitcherScreen.tsx +1 -1
- package/src/ui/screens/OxyAuthScreen.tsx +2 -7
- 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
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
import type { OxyServices } from '../OxyServices';
|
|
15
15
|
import { tokenService } from './TokenService';
|
|
16
16
|
import type { ClientSession, SessionLoginResponse } from '../../models/session';
|
|
17
|
-
import type { User } from '
|
|
17
|
+
import type { User } from '../../shared';
|
|
18
18
|
|
|
19
19
|
export interface Session {
|
|
20
20
|
sessionId: string;
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import type { OxyConfig } from '../../models/interfaces';
|
|
11
|
-
import type { User, SearchProfilesResponse, PaginationInfo } from '
|
|
11
|
+
import type { User, SearchProfilesResponse, PaginationInfo } from '../../shared';
|
|
12
12
|
import { HttpService } from '../HttpService';
|
|
13
13
|
|
|
14
14
|
export interface PaginationParams {
|
package/src/crypto/keyManager.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import { ec as EC } from 'elliptic';
|
|
9
9
|
import type { ECKeyPair } from 'elliptic';
|
|
10
10
|
import { Platform } from 'react-native';
|
|
11
|
+
import { shortenPublicKey as sharedShortenPublicKey } from '../shared';
|
|
11
12
|
|
|
12
13
|
// Lazy imports for React Native specific modules
|
|
13
14
|
let SecureStore: typeof import('expo-secure-store') | null = null;
|
|
@@ -540,8 +541,7 @@ export class KeyManager {
|
|
|
540
541
|
*/
|
|
541
542
|
static shortenPublicKey(publicKey: string): string {
|
|
542
543
|
// Use shared utility
|
|
543
|
-
|
|
544
|
-
return shortenPublicKey(publicKey);
|
|
544
|
+
return sharedShortenPublicKey(publicKey);
|
|
545
545
|
}
|
|
546
546
|
}
|
|
547
547
|
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Used for authenticating requests and proving identity ownership.
|
|
6
6
|
*
|
|
7
7
|
* Note: This service handles SIGNING (requires private key access via KeyManager).
|
|
8
|
-
* For VERIFICATION, use
|
|
8
|
+
* For VERIFICATION, use the shared SignatureService from '../shared' instead.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { ec as EC } from 'elliptic';
|
|
@@ -15,13 +15,15 @@ import {
|
|
|
15
15
|
buildRegistrationMessage,
|
|
16
16
|
buildRequestMessage,
|
|
17
17
|
getCryptoAdapter,
|
|
18
|
+
SignatureService as SharedSignatureService,
|
|
19
|
+
isTimestampFresh,
|
|
18
20
|
type SignedMessage,
|
|
19
|
-
} from '
|
|
21
|
+
} from '../shared';
|
|
20
22
|
|
|
21
23
|
const ec = new EC('secp256k1');
|
|
22
24
|
|
|
23
25
|
// Re-export shared types
|
|
24
|
-
export type { SignedMessage } from '
|
|
26
|
+
export type { SignedMessage } from '../shared';
|
|
25
27
|
|
|
26
28
|
export interface AuthChallenge {
|
|
27
29
|
challenge: string;
|
|
@@ -84,7 +86,6 @@ export class SignatureService {
|
|
|
84
86
|
* Uses shared SignatureService for verification
|
|
85
87
|
*/
|
|
86
88
|
static async verify(message: string, signature: string, publicKey: string): Promise<boolean> {
|
|
87
|
-
const { SignatureService: SharedSignatureService } = await import('@oxyhq/shared');
|
|
88
89
|
return SharedSignatureService.verify(message, signature, publicKey);
|
|
89
90
|
}
|
|
90
91
|
|
|
@@ -118,7 +119,6 @@ export class SignatureService {
|
|
|
118
119
|
signedMessage: SignedMessage,
|
|
119
120
|
maxAgeMs: number = 5 * 60 * 1000 // 5 minutes default
|
|
120
121
|
): Promise<boolean> {
|
|
121
|
-
const { SignatureService: SharedSignatureService, isTimestampFresh } = await import('@oxyhq/shared');
|
|
122
122
|
const { message, signature, publicKey, timestamp } = signedMessage;
|
|
123
123
|
|
|
124
124
|
// Check timestamp freshness
|
|
@@ -161,7 +161,6 @@ export class SignatureService {
|
|
|
161
161
|
response: AuthChallenge,
|
|
162
162
|
maxAgeMs: number = 5 * 60 * 1000
|
|
163
163
|
): Promise<boolean> {
|
|
164
|
-
const { SignatureService: SharedSignatureService } = await import('@oxyhq/shared');
|
|
165
164
|
const { challenge: signature, publicKey, timestamp } = response;
|
|
166
165
|
return SharedSignatureService.verifyChallengeResponse(
|
|
167
166
|
publicKey,
|
package/src/index.ts
CHANGED
|
@@ -48,8 +48,10 @@ 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
|
-
// Note: User and LoginResponse should be imported from @oxyhq/shared
|
|
53
55
|
export type {
|
|
54
56
|
OxyConfig,
|
|
55
57
|
Notification,
|
package/src/models/interfaces.ts
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Services Package Interfaces
|
|
3
3
|
*
|
|
4
4
|
* Package-specific interfaces. For shared models (User, Session, etc.),
|
|
5
|
-
* import directly from
|
|
5
|
+
* import directly from the shared module:
|
|
6
6
|
*
|
|
7
|
-
* import { User, LoginResponse, Session } from '
|
|
7
|
+
* import { User, LoginResponse, Session } from '../shared';
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
export interface OxyConfig {
|
|
@@ -30,8 +30,8 @@ export interface OxyConfig {
|
|
|
30
30
|
onRequestError?: (url: string, method: string, error: Error) => void;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
// Note: User and LoginResponse are in
|
|
34
|
-
// Import them directly: import { User, LoginResponse } from '
|
|
33
|
+
// Note: User and LoginResponse are in the shared module
|
|
34
|
+
// Import them directly: import { User, LoginResponse } from '../shared';
|
|
35
35
|
|
|
36
36
|
export interface Notification {
|
|
37
37
|
id: string;
|
|
@@ -104,8 +104,8 @@ export interface TransactionResponse {
|
|
|
104
104
|
transaction: Transaction;
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
// Note: PaginationInfo and SearchProfilesResponse are in
|
|
108
|
-
// Import them directly: import { PaginationInfo, SearchProfilesResponse } from '
|
|
107
|
+
// Note: PaginationInfo and SearchProfilesResponse are in the shared module
|
|
108
|
+
// Import them directly: import { PaginationInfo, SearchProfilesResponse } from '../shared';
|
|
109
109
|
|
|
110
110
|
export interface KarmaRule {
|
|
111
111
|
id: string;
|
|
@@ -435,7 +435,7 @@ export interface AssetUploadProgress {
|
|
|
435
435
|
}
|
|
436
436
|
|
|
437
437
|
// Device Session interfaces
|
|
438
|
-
// Note: User type should be imported from
|
|
438
|
+
// Note: User type should be imported from the shared module
|
|
439
439
|
export interface DeviceSession {
|
|
440
440
|
sessionId: string;
|
|
441
441
|
deviceId: string;
|
|
@@ -450,7 +450,7 @@ export interface DeviceSession {
|
|
|
450
450
|
username: string;
|
|
451
451
|
avatar?: string;
|
|
452
452
|
[key: string]: unknown;
|
|
453
|
-
}; // Partial User - import full User type from
|
|
453
|
+
}; // Partial User - import full User type from '../shared' if needed
|
|
454
454
|
createdAt?: string;
|
|
455
455
|
}
|
|
456
456
|
|
|
@@ -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
|
+
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Signature Verification Service
|
|
3
|
+
*
|
|
4
|
+
* Unified signature verification used by both backend (API) and SDK.
|
|
5
|
+
* Uses platform adapters for crypto operations while keeping message construction shared.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { ec as EC } from 'elliptic';
|
|
9
|
+
import { getCryptoAdapter, PlatformDetector } from './platform';
|
|
10
|
+
import {
|
|
11
|
+
buildAuthMessage,
|
|
12
|
+
buildRegistrationMessage,
|
|
13
|
+
buildRequestMessage,
|
|
14
|
+
isTimestampFresh,
|
|
15
|
+
} from './messageBuilders';
|
|
16
|
+
|
|
17
|
+
const ec = new EC('secp256k1');
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Maximum age for signed messages (5 minutes)
|
|
21
|
+
*/
|
|
22
|
+
export const MAX_SIGNATURE_AGE_MS = 5 * 60 * 1000;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Challenge TTL (5 minutes)
|
|
26
|
+
*/
|
|
27
|
+
export const CHALLENGE_TTL_MS = 5 * 60 * 1000;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Signature Service
|
|
31
|
+
* Provides signature verification that works across all platforms
|
|
32
|
+
*/
|
|
33
|
+
export class SignatureService {
|
|
34
|
+
/**
|
|
35
|
+
* Verify an ECDSA signature
|
|
36
|
+
*
|
|
37
|
+
* @param message - The original message that was signed
|
|
38
|
+
* @param signature - The signature in DER format (hex encoded)
|
|
39
|
+
* @param publicKey - The public key (hex encoded, uncompressed)
|
|
40
|
+
* @returns true if the signature is valid
|
|
41
|
+
*/
|
|
42
|
+
static async verify(
|
|
43
|
+
message: string,
|
|
44
|
+
signature: string,
|
|
45
|
+
publicKey: string
|
|
46
|
+
): Promise<boolean> {
|
|
47
|
+
try {
|
|
48
|
+
const key = ec.keyFromPublic(publicKey, 'hex');
|
|
49
|
+
const adapter = await getCryptoAdapter();
|
|
50
|
+
const messageHash = await adapter.sha256(message);
|
|
51
|
+
return key.verify(messageHash, signature);
|
|
52
|
+
} catch {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Synchronous signature verification (Node.js only)
|
|
59
|
+
* Uses Node.js crypto module directly for hashing
|
|
60
|
+
*/
|
|
61
|
+
static verifySync(
|
|
62
|
+
message: string,
|
|
63
|
+
signature: string,
|
|
64
|
+
publicKey: string
|
|
65
|
+
): boolean {
|
|
66
|
+
if (!PlatformDetector.isNode()) {
|
|
67
|
+
throw new Error(
|
|
68
|
+
'verifySync should only be used in Node.js. Use verify() in other environments.'
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
// eslint-disable-next-line @typescript-eslint/no-implied-eval
|
|
74
|
+
const getCrypto = new Function('return require("crypto")');
|
|
75
|
+
const crypto = getCrypto();
|
|
76
|
+
const key = ec.keyFromPublic(publicKey, 'hex');
|
|
77
|
+
const messageHash = crypto.createHash('sha256').update(message).digest('hex');
|
|
78
|
+
return key.verify(messageHash, signature);
|
|
79
|
+
} catch {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Verify an authentication challenge response
|
|
86
|
+
*
|
|
87
|
+
* @param publicKey - The user's public key
|
|
88
|
+
* @param challenge - The original challenge string
|
|
89
|
+
* @param signature - The signature of the auth message
|
|
90
|
+
* @param timestamp - The timestamp when the signature was created
|
|
91
|
+
* @param maxAgeMs - Maximum age of the signature in milliseconds
|
|
92
|
+
* @returns true if the challenge response is valid
|
|
93
|
+
*/
|
|
94
|
+
static async verifyChallengeResponse(
|
|
95
|
+
publicKey: string,
|
|
96
|
+
challenge: string,
|
|
97
|
+
signature: string,
|
|
98
|
+
timestamp: number,
|
|
99
|
+
maxAgeMs: number = CHALLENGE_TTL_MS
|
|
100
|
+
): Promise<boolean> {
|
|
101
|
+
// Check timestamp freshness
|
|
102
|
+
if (!isTimestampFresh(timestamp, maxAgeMs)) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Build the canonical message
|
|
107
|
+
const message = buildAuthMessage(publicKey, challenge, timestamp);
|
|
108
|
+
|
|
109
|
+
// Verify the signature
|
|
110
|
+
return this.verify(message, signature, publicKey);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Synchronous challenge response verification (Node.js only)
|
|
115
|
+
*/
|
|
116
|
+
static verifyChallengeResponseSync(
|
|
117
|
+
publicKey: string,
|
|
118
|
+
challenge: string,
|
|
119
|
+
signature: string,
|
|
120
|
+
timestamp: number,
|
|
121
|
+
maxAgeMs: number = CHALLENGE_TTL_MS
|
|
122
|
+
): boolean {
|
|
123
|
+
// Check timestamp freshness
|
|
124
|
+
if (!isTimestampFresh(timestamp, maxAgeMs)) {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Build the canonical message
|
|
129
|
+
const message = buildAuthMessage(publicKey, challenge, timestamp);
|
|
130
|
+
|
|
131
|
+
// Verify the signature
|
|
132
|
+
return this.verifySync(message, signature, publicKey);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Verify a registration signature
|
|
137
|
+
* Signature format: oxy:register:{publicKey}:{timestamp}
|
|
138
|
+
*/
|
|
139
|
+
static async verifyRegistrationSignature(
|
|
140
|
+
publicKey: string,
|
|
141
|
+
signature: string,
|
|
142
|
+
timestamp: number,
|
|
143
|
+
maxAgeMs: number = MAX_SIGNATURE_AGE_MS
|
|
144
|
+
): Promise<boolean> {
|
|
145
|
+
// Check timestamp freshness
|
|
146
|
+
if (!isTimestampFresh(timestamp, maxAgeMs)) {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Build the canonical message
|
|
151
|
+
const message = buildRegistrationMessage(publicKey, timestamp);
|
|
152
|
+
|
|
153
|
+
// Verify the signature
|
|
154
|
+
return this.verify(message, signature, publicKey);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Synchronous registration signature verification (Node.js only)
|
|
159
|
+
*/
|
|
160
|
+
static verifyRegistrationSignatureSync(
|
|
161
|
+
publicKey: string,
|
|
162
|
+
signature: string,
|
|
163
|
+
timestamp: number,
|
|
164
|
+
maxAgeMs: number = MAX_SIGNATURE_AGE_MS
|
|
165
|
+
): boolean {
|
|
166
|
+
// Check timestamp freshness
|
|
167
|
+
if (!isTimestampFresh(timestamp, maxAgeMs)) {
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Build the canonical message
|
|
172
|
+
const message = buildRegistrationMessage(publicKey, timestamp);
|
|
173
|
+
|
|
174
|
+
// Verify the signature
|
|
175
|
+
return this.verifySync(message, signature, publicKey);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Verify a signed request
|
|
180
|
+
* Used for authenticated API operations
|
|
181
|
+
*/
|
|
182
|
+
static async verifyRequestSignature(
|
|
183
|
+
publicKey: string,
|
|
184
|
+
data: Record<string, unknown>,
|
|
185
|
+
signature: string,
|
|
186
|
+
timestamp: number,
|
|
187
|
+
maxAgeMs: number = MAX_SIGNATURE_AGE_MS
|
|
188
|
+
): Promise<boolean> {
|
|
189
|
+
// Check timestamp freshness
|
|
190
|
+
if (!isTimestampFresh(timestamp, maxAgeMs)) {
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Build the canonical message
|
|
195
|
+
const message = buildRequestMessage(publicKey, timestamp, data);
|
|
196
|
+
|
|
197
|
+
// Verify the signature
|
|
198
|
+
return this.verify(message, signature, publicKey);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Synchronous request signature verification (Node.js only)
|
|
203
|
+
*/
|
|
204
|
+
static verifyRequestSignatureSync(
|
|
205
|
+
publicKey: string,
|
|
206
|
+
data: Record<string, unknown>,
|
|
207
|
+
signature: string,
|
|
208
|
+
timestamp: number,
|
|
209
|
+
maxAgeMs: number = MAX_SIGNATURE_AGE_MS
|
|
210
|
+
): boolean {
|
|
211
|
+
// Check timestamp freshness
|
|
212
|
+
if (!isTimestampFresh(timestamp, maxAgeMs)) {
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Build the canonical message
|
|
217
|
+
const message = buildRequestMessage(publicKey, timestamp, data);
|
|
218
|
+
|
|
219
|
+
// Verify the signature
|
|
220
|
+
return this.verifySync(message, signature, publicKey);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Validate that a string is a valid public key
|
|
225
|
+
*/
|
|
226
|
+
static isValidPublicKey(publicKey: string): boolean {
|
|
227
|
+
try {
|
|
228
|
+
ec.keyFromPublic(publicKey, 'hex');
|
|
229
|
+
return true;
|
|
230
|
+
} catch {
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @oxyhq/shared
|
|
3
|
+
*
|
|
4
|
+
* Shared utilities, models, and crypto primitives for OxyHQ packages.
|
|
5
|
+
*
|
|
6
|
+
* This package provides:
|
|
7
|
+
* - Canonical data models (User, Session, ChallengePayload, etc.)
|
|
8
|
+
* - Unified signature verification across platforms
|
|
9
|
+
* - Platform-agnostic crypto adapters
|
|
10
|
+
* - Shared utility functions
|
|
11
|
+
* - Canonical message builders for signing
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
// Models
|
|
15
|
+
export * from './models/index';
|
|
16
|
+
|
|
17
|
+
// Crypto
|
|
18
|
+
export * from './crypto/signature';
|
|
19
|
+
export * from './crypto/messageBuilders';
|
|
20
|
+
export * from './crypto/platform';
|
|
21
|
+
export { getCryptoAdapter, PlatformDetector } from './crypto/platform';
|
|
22
|
+
|
|
23
|
+
// Utils
|
|
24
|
+
export * from './utils/index';
|
|
25
|
+
|
|
26
|
+
// Transport
|
|
27
|
+
export * from './transport/index';
|
|
28
|
+
|