@oxyhq/core 4.0.1 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/dist/cjs/.tsbuildinfo +1 -1
  2. package/dist/cjs/HttpService.js +6 -18
  3. package/dist/cjs/OxyServices.base.js +0 -21
  4. package/dist/cjs/crypto/keyManager.js +7 -7
  5. package/dist/cjs/crypto/polyfill.js +6 -5
  6. package/dist/cjs/crypto/signatureService.js +44 -220
  7. package/dist/cjs/index.js +4 -8
  8. package/dist/cjs/mixins/OxyServices.accounts.js +90 -0
  9. package/dist/cjs/mixins/OxyServices.assets.js +68 -2
  10. package/dist/cjs/mixins/OxyServices.auth.js +3 -3
  11. package/dist/cjs/mixins/OxyServices.civic.js +3 -3
  12. package/dist/cjs/mixins/OxyServices.language.js +2 -2
  13. package/dist/cjs/mixins/OxyServices.utility.js +7 -95
  14. package/dist/cjs/utils/cacheKey.js +17 -19
  15. package/dist/cjs/utils/deviceManager.js +2 -2
  16. package/dist/cjs/utils/platform.js +0 -14
  17. package/dist/esm/.tsbuildinfo +1 -1
  18. package/dist/esm/HttpService.js +6 -18
  19. package/dist/esm/OxyServices.base.js +0 -21
  20. package/dist/esm/crypto/keyManager.js +4 -4
  21. package/dist/esm/crypto/polyfill.js +5 -4
  22. package/dist/esm/crypto/signatureService.js +39 -214
  23. package/dist/esm/index.js +1 -2
  24. package/dist/esm/mixins/OxyServices.accounts.js +90 -0
  25. package/dist/esm/mixins/OxyServices.assets.js +67 -1
  26. package/dist/esm/mixins/OxyServices.auth.js +1 -1
  27. package/dist/esm/mixins/OxyServices.civic.js +3 -3
  28. package/dist/esm/mixins/OxyServices.language.js +1 -1
  29. package/dist/esm/mixins/OxyServices.utility.js +6 -94
  30. package/dist/esm/utils/cacheKey.js +17 -19
  31. package/dist/esm/utils/deviceManager.js +1 -1
  32. package/dist/esm/utils/platform.js +0 -12
  33. package/dist/types/.tsbuildinfo +1 -1
  34. package/dist/types/HttpService.d.ts +3 -6
  35. package/dist/types/OxyServices.base.d.ts +0 -17
  36. package/dist/types/crypto/polyfill.d.ts +2 -2
  37. package/dist/types/crypto/signatureService.d.ts +18 -84
  38. package/dist/types/index.d.ts +4 -5
  39. package/dist/types/mixins/OxyServices.accounts.d.ts +66 -5
  40. package/dist/types/mixins/OxyServices.analytics.d.ts +0 -2
  41. package/dist/types/mixins/OxyServices.appData.d.ts +0 -2
  42. package/dist/types/mixins/OxyServices.assets.d.ts +42 -3
  43. package/dist/types/mixins/OxyServices.auth.d.ts +0 -2
  44. package/dist/types/mixins/OxyServices.civic.d.ts +3 -5
  45. package/dist/types/mixins/OxyServices.connectedApps.d.ts +0 -2
  46. package/dist/types/mixins/OxyServices.contacts.d.ts +0 -2
  47. package/dist/types/mixins/OxyServices.devices.d.ts +0 -2
  48. package/dist/types/mixins/OxyServices.features.d.ts +0 -2
  49. package/dist/types/mixins/OxyServices.fedcm.d.ts +0 -2
  50. package/dist/types/mixins/OxyServices.identity.d.ts +8 -5
  51. package/dist/types/mixins/OxyServices.language.d.ts +0 -2
  52. package/dist/types/mixins/OxyServices.links.d.ts +0 -2
  53. package/dist/types/mixins/OxyServices.location.d.ts +0 -2
  54. package/dist/types/mixins/OxyServices.nodes.d.ts +0 -44
  55. package/dist/types/mixins/OxyServices.payment.d.ts +0 -2
  56. package/dist/types/mixins/OxyServices.privacy.d.ts +0 -2
  57. package/dist/types/mixins/OxyServices.redirect.d.ts +0 -2
  58. package/dist/types/mixins/OxyServices.reputation.d.ts +0 -2
  59. package/dist/types/mixins/OxyServices.security.d.ts +0 -2
  60. package/dist/types/mixins/OxyServices.silent.d.ts +0 -2
  61. package/dist/types/mixins/OxyServices.sso.d.ts +0 -2
  62. package/dist/types/mixins/OxyServices.topics.d.ts +0 -2
  63. package/dist/types/mixins/OxyServices.user.d.ts +0 -2
  64. package/dist/types/mixins/OxyServices.utility.d.ts +0 -32
  65. package/dist/types/models/interfaces.d.ts +18 -0
  66. package/dist/types/server/auth.d.ts +0 -6
  67. package/dist/types/server/index.d.ts +1 -1
  68. package/dist/types/utils/cacheKey.d.ts +6 -7
  69. package/dist/types/utils/platform.d.ts +0 -8
  70. package/package.json +4 -7
  71. package/src/HttpService.ts +6 -22
  72. package/src/OxyServices.base.ts +0 -23
  73. package/src/__tests__/httpServiceCache.test.ts +0 -19
  74. package/src/crypto/__tests__/keyManager.atomicity.test.ts +2 -1
  75. package/src/crypto/__tests__/keyManager.test.ts +9 -7
  76. package/src/crypto/__tests__/signChallengeShared.test.ts +2 -1
  77. package/src/crypto/__tests__/signedRecord.test.ts +37 -150
  78. package/src/crypto/keyManager.ts +28 -17
  79. package/src/crypto/polyfill.ts +5 -4
  80. package/src/crypto/signatureService.ts +67 -255
  81. package/src/index.ts +4 -3
  82. package/src/mixins/OxyServices.accounts.ts +136 -3
  83. package/src/mixins/OxyServices.assets.ts +96 -2
  84. package/src/mixins/OxyServices.auth.ts +1 -1
  85. package/src/mixins/OxyServices.civic.ts +6 -17
  86. package/src/mixins/OxyServices.identity.ts +8 -2
  87. package/src/mixins/OxyServices.language.ts +1 -1
  88. package/src/mixins/OxyServices.nodes.ts +1 -12
  89. package/src/mixins/OxyServices.utility.ts +6 -119
  90. package/src/mixins/__tests__/OxyServices.civic.test.ts +2 -2
  91. package/src/mixins/__tests__/OxyServices.serviceAssetMetadata.test.ts +116 -0
  92. package/src/mixins/__tests__/accounts.test.ts +120 -0
  93. package/src/models/interfaces.ts +19 -0
  94. package/src/server/auth.ts +0 -7
  95. package/src/server/index.ts +0 -1
  96. package/src/utils/__tests__/cacheKey.test.ts +0 -0
  97. package/src/utils/cacheKey.ts +16 -21
  98. package/src/utils/deviceManager.ts +1 -1
  99. package/src/utils/platform.ts +0 -14
  100. package/dist/cjs/crypto/canonicalJson.js +0 -107
  101. package/dist/cjs/utils/platformCrypto.js +0 -165
  102. package/dist/cjs/utils/platformCrypto.native.js +0 -123
  103. package/dist/esm/crypto/canonicalJson.js +0 -104
  104. package/dist/esm/utils/platformCrypto.js +0 -125
  105. package/dist/esm/utils/platformCrypto.native.js +0 -80
  106. package/dist/types/crypto/canonicalJson.d.ts +0 -44
  107. package/dist/types/utils/platformCrypto.d.ts +0 -87
  108. package/dist/types/utils/platformCrypto.native.d.ts +0 -54
  109. package/src/crypto/__tests__/canonicalJson.test.ts +0 -116
  110. package/src/crypto/canonicalJson.ts +0 -120
  111. package/src/utils/platformCrypto.native.ts +0 -101
  112. package/src/utils/platformCrypto.ts +0 -145
@@ -1,45 +1,3 @@
1
- /**
2
- * User-Node Methods Mixin (self-sovereign identity layer — Fase 5 user nodes)
3
- *
4
- * The client surface for a user's personal data NODE — the decentralised store
5
- * that holds an authentic copy of their signed-record chain. Commons drives all
6
- * of this:
7
- *
8
- * - {@link OxyServicesNodesMixin.registerNode} registers (or re-registers) a
9
- * SELF-HOSTED node. Registration is NOT a bespoke endpoint — it is a signed
10
- * `type:'node'` v2 record (`collection: 'app.oxy.node'`, `rkey: 'self'`,
11
- * last-writer-wins) published through the EXISTING `POST /identity/records`
12
- * path; the server verifies it and materializes the operational
13
- * {@link UserNodeStatus} cache as a side effect, so the registration's
14
- * authority is the user's own signature, never an Oxy grant.
15
- * - {@link OxyServicesNodesMixin.getMyNode} reads the caller's cached node
16
- * status (`GET /nodes/me`) — the fast, stale-but-instant projection plus the
17
- * live liveness badge Oxy maintains with background probes.
18
- * - {@link OxyServicesNodesMixin.removeMyNode} revokes the registration
19
- * (`DELETE /nodes/me`) so the node leaves the DID document and the liveness
20
- * sweeps.
21
- * - {@link OxyServicesNodesMixin.provisionManagedVault} asks Oxy to operate a
22
- * MANAGED vault on the caller's behalf (`POST /nodes/managed`) — the
23
- * "Create your vault" convenience for non-technical users (Oxy custodial-signs
24
- * the node record; `managed:true, controller:'oxy'`).
25
- * - {@link OxyServicesNodesMixin.notifyNodeIngest} sends an unauthenticated
26
- * HINT (`POST /nodes/ingest/notify/:userId`) that a user's node has new
27
- * records; the server fully re-verifies before ingesting, so the hint can
28
- * never inject data.
29
- *
30
- * `registerNode` signs on the caller's per-subject hash chain with the on-device
31
- * identity key (reusing {@link SignatureService.signRecordV2} — the same
32
- * `ES256K-DER-SHA256` scheme + {@link signedRecordSigningInput} the identity and
33
- * civic mixins use), so it is NATIVE-ONLY: it throws on web (where `KeyManager`
34
- * has no key) and when no user is authenticated. Reading the node status,
35
- * revoking, provisioning a managed vault, and sending an ingest hint are plain
36
- * authenticated/public requests with no signing.
37
- *
38
- * The wire shapes here are API-INTERNAL (the F5 user-node surface is not yet a
39
- * published `@oxyhq/contracts` schema), so {@link UserNodeStatus} mirrors the
40
- * server's `serializeNode` projection exactly. Dates cross the wire as ISO
41
- * strings.
42
- */
43
1
  import type { OxyServicesBase } from '../OxyServices.base';
44
2
  /** How Oxy and the node move records: the node pulls (default), or Oxy pushes. */
45
3
  export type UserNodeMode = 'pull' | 'push';
@@ -223,8 +181,6 @@ export declare function OxyServicesNodesMixin<T extends typeof OxyServicesBase>(
223
181
  hasValidToken(): boolean;
224
182
  getAccessToken(): string | null;
225
183
  getAccessTokenExpiry(): number | null;
226
- setActingAs(userId: string | null): void;
227
- getActingAs(): string | null;
228
184
  waitForAuth(timeoutMs?: number): Promise<boolean>;
229
185
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
230
186
  maxRetries?: number;
@@ -100,8 +100,6 @@ export declare function OxyServicesPaymentMixin<T extends typeof OxyServicesBase
100
100
  hasValidToken(): boolean;
101
101
  getAccessToken(): string | null;
102
102
  getAccessTokenExpiry(): number | null;
103
- setActingAs(userId: string | null): void;
104
- getActingAs(): string | null;
105
103
  waitForAuth(timeoutMs?: number): Promise<boolean>;
106
104
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
107
105
  maxRetries?: number;
@@ -129,8 +129,6 @@ export declare function OxyServicesPrivacyMixin<T extends typeof OxyServicesBase
129
129
  hasValidToken(): boolean;
130
130
  getAccessToken(): string | null;
131
131
  getAccessTokenExpiry(): number | null;
132
- setActingAs(userId: string | null): void;
133
- getActingAs(): string | null;
134
132
  waitForAuth(timeoutMs?: number): Promise<boolean>;
135
133
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
136
134
  maxRetries?: number;
@@ -72,8 +72,6 @@ export declare function OxyServicesRedirectAuthMixin<T extends typeof OxyService
72
72
  hasValidToken(): boolean;
73
73
  getAccessToken(): string | null;
74
74
  getAccessTokenExpiry(): number | null;
75
- setActingAs(userId: string | null): void;
76
- getActingAs(): string | null;
77
75
  waitForAuth(timeoutMs?: number): Promise<boolean>;
78
76
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
79
77
  maxRetries?: number;
@@ -418,8 +418,6 @@ export declare function OxyServicesReputationMixin<T extends typeof OxyServicesB
418
418
  hasValidToken(): boolean;
419
419
  getAccessToken(): string | null;
420
420
  getAccessTokenExpiry(): number | null;
421
- setActingAs(userId: string | null): void;
422
- getActingAs(): string | null;
423
421
  waitForAuth(timeoutMs?: number): Promise<boolean>;
424
422
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
425
423
  maxRetries?: number;
@@ -67,8 +67,6 @@ export declare function OxyServicesSecurityMixin<T extends typeof OxyServicesBas
67
67
  hasValidToken(): boolean;
68
68
  getAccessToken(): string | null;
69
69
  getAccessTokenExpiry(): number | null;
70
- setActingAs(userId: string | null): void;
71
- getActingAs(): string | null;
72
70
  waitForAuth(timeoutMs?: number): Promise<boolean>;
73
71
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
74
72
  maxRetries?: number;
@@ -110,8 +110,6 @@ export declare function OxyServicesSilentAuthMixin<T extends typeof OxyServicesB
110
110
  hasValidToken(): boolean;
111
111
  getAccessToken(): string | null;
112
112
  getAccessTokenExpiry(): number | null;
113
- setActingAs(userId: string | null): void;
114
- getActingAs(): string | null;
115
113
  waitForAuth(timeoutMs?: number): Promise<boolean>;
116
114
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
117
115
  maxRetries?: number;
@@ -95,8 +95,6 @@ export declare function OxyServicesSsoMixin<T extends typeof OxyServicesBase>(Ba
95
95
  hasValidToken(): boolean;
96
96
  getAccessToken(): string | null;
97
97
  getAccessTokenExpiry(): number | null;
98
- setActingAs(userId: string | null): void;
99
- getActingAs(): string | null;
100
98
  waitForAuth(timeoutMs?: number): Promise<boolean>;
101
99
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
102
100
  maxRetries?: number;
@@ -93,8 +93,6 @@ export declare function OxyServicesTopicsMixin<T extends typeof OxyServicesBase>
93
93
  hasValidToken(): boolean;
94
94
  getAccessToken(): string | null;
95
95
  getAccessTokenExpiry(): number | null;
96
- setActingAs(userId: string | null): void;
97
- getActingAs(): string | null;
98
96
  waitForAuth(timeoutMs?: number): Promise<boolean>;
99
97
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
100
98
  maxRetries?: number;
@@ -364,8 +364,6 @@ export declare function OxyServicesUserMixin<T extends typeof OxyServicesBase>(B
364
364
  hasValidToken(): boolean;
365
365
  getAccessToken(): string | null;
366
366
  getAccessTokenExpiry(): number | null;
367
- setActingAs(userId: string | null): void;
368
- getActingAs(): string | null;
369
367
  waitForAuth(timeoutMs?: number): Promise<boolean>;
370
368
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
371
369
  maxRetries?: number;
@@ -1,15 +1,5 @@
1
1
  import type { ApiError, User } from '../models/interfaces';
2
2
  import type { OxyServicesBase } from '../OxyServices.base';
3
- /**
4
- * Result from the account acting-as verification endpoint
5
- * (`GET /accounts/verify-acting-as`). Indicates whether a user is authorized to
6
- * act as a given account. The `account:act_as` capability is granted only to the
7
- * `owner`, `admin`, and `editor` account roles.
8
- */
9
- interface ActingAsVerification {
10
- authorized: boolean;
11
- role: 'owner' | 'admin' | 'editor';
12
- }
13
3
  /**
14
4
  * Result from the service-acting-as verification endpoint.
15
5
  * Confirms that a given service app holds an active delegation grant for
@@ -76,11 +66,6 @@ interface AuthMiddlewareOptions {
76
66
  }
77
67
  export declare function OxyServicesUtilityMixin<T extends typeof OxyServicesBase>(Base: T): {
78
68
  new (...args: any[]): {
79
- /** @internal In-memory cache for acting-as verification results (TTL: 5 min) */
80
- _actingAsCache: Map<string, {
81
- result: ActingAsVerification | null;
82
- expiresAt: number;
83
- }>;
84
69
  /**
85
70
  * In-memory cache for service-acting-as verification.
86
71
  * Negative results are cached for 1min to avoid hammering the verify
@@ -92,14 +77,6 @@ export declare function OxyServicesUtilityMixin<T extends typeof OxyServicesBase
92
77
  result: ServiceActingAsVerification | null;
93
78
  expiresAt: number;
94
79
  }>;
95
- /**
96
- * Verify that a user is authorized to act as an account (direct membership
97
- * or inherited via an ancestor). Backed by `GET /accounts/verify-acting-as`.
98
- * Results are cached in-memory for 5 minutes to avoid repeated API calls.
99
- *
100
- * @internal Used by the auth() middleware — not part of the public API
101
- */
102
- verifyActingAs(userId: string, accountId: string): Promise<ActingAsVerification | null>;
103
80
  /**
104
81
  * Verify that a service app holds an active delegation grant authorising
105
82
  * it to act on behalf of `userId`. Returns the grant (with allowed scopes)
@@ -281,8 +258,6 @@ export declare function OxyServicesUtilityMixin<T extends typeof OxyServicesBase
281
258
  hasValidToken(): boolean;
282
259
  getAccessToken(): string | null;
283
260
  getAccessTokenExpiry(): number | null;
284
- setActingAs(userId: string | null): void;
285
- getActingAs(): string | null;
286
261
  waitForAuth(timeoutMs?: number): Promise<boolean>;
287
262
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
288
263
  maxRetries?: number;
@@ -313,13 +288,6 @@ interface AuthReq {
313
288
  userId: string;
314
289
  scopes: string[];
315
290
  };
316
- actingAs?: {
317
- userId: string;
318
- role: string;
319
- };
320
- originalUser?: {
321
- id: string;
322
- } & Partial<User>;
323
291
  }
324
292
  interface AuthRes {
325
293
  status(code: number): AuthRes;
@@ -447,6 +447,24 @@ export interface AssetUpdateVisibilityResponse {
447
447
  updatedAt: string;
448
448
  };
449
449
  }
450
+ /**
451
+ * Minimal, service-token-scoped asset metadata returned by
452
+ * `POST /assets/service/by-ids`.
453
+ *
454
+ * Resolves an Oxy asset `id` to its content-addressed identity (`sha256`),
455
+ * MIME type, byte `size`, and storage `status`. Used by server-to-server
456
+ * callers (e.g. Mention's MTN Protocol blob-ref resolution) that hold a
457
+ * `files:read`-scoped service token rather than a user session. Unknown or
458
+ * deleted ids are omitted from the response (never error the whole batch),
459
+ * so the result may be shorter than the requested id list.
460
+ */
461
+ export interface ServiceAssetMetadata {
462
+ id: string;
463
+ sha256: string;
464
+ mime: string;
465
+ size: number;
466
+ status: 'active' | 'trash';
467
+ }
450
468
  /**
451
469
  * Account storage usage (server-side usage, not local AsyncStorage)
452
470
  */
@@ -8,10 +8,6 @@ export interface OxyRequestUser {
8
8
  avatar?: string;
9
9
  [key: string]: unknown;
10
10
  }
11
- export interface OxyActingAsContext {
12
- userId: string;
13
- role: 'owner' | 'admin' | 'editor';
14
- }
15
11
  export interface OxyServiceAppContext {
16
12
  appId: string;
17
13
  appName: string;
@@ -25,8 +21,6 @@ export interface OxyServiceActingAsContext {
25
21
  export interface OxyAuthRequest extends Request {
26
22
  userId?: string | null;
27
23
  user?: OxyRequestUser | null;
28
- originalUser?: OxyRequestUser | null;
29
- actingAs?: OxyActingAsContext;
30
24
  accessToken?: string;
31
25
  sessionId?: string | null;
32
26
  serviceApp?: OxyServiceAppContext;
@@ -15,7 +15,7 @@
15
15
  * ```
16
16
  */
17
17
  export { createOptionalOxyAuth, createOxyAuthMiddleware, getOxyUserId, getRequiredOxyUserId, isOxyAuthenticated, requireOxyAuth, } from './auth';
18
- export type { OxyActingAsContext, OxyAuthenticatedRequest, OxyAuthMiddlewareOptions, OxyAuthRequest, OxyRequestUser, OxyServiceActingAsContext, OxyServiceAppContext, } from './auth';
18
+ export type { OxyAuthenticatedRequest, OxyAuthMiddlewareOptions, OxyAuthRequest, OxyRequestUser, OxyServiceActingAsContext, OxyServiceAppContext, } from './auth';
19
19
  export { createOxyRateLimit } from './rateLimit';
20
20
  export type { OxyRateLimitOptions } from './rateLimit';
21
21
  export { assertSafePublicUrl, isBlockedIp, safeFetch, SsrfRejection, UpstreamError, ALLOWED_PORTS, ALLOWED_PROTOCOLS, BLOCKED_HOSTNAMES, DEFAULT_USER_AGENT, MAX_REDIRECTS, MAX_URL_LENGTH, UPSTREAM_HEADERS_TIMEOUT_MS, } from './safeFetch';
@@ -3,8 +3,8 @@
3
3
  *
4
4
  * Extracted from {@link HttpService} so the identity-tag derivation is a pure,
5
5
  * independently testable function with no dependency on instance/token state.
6
- * The HTTP service injects the live access token and acting-as id; everything
7
- * here is referentially transparent given those inputs.
6
+ * The HTTP service injects the live access token; everything here is
7
+ * referentially transparent given that input.
8
8
  */
9
9
  /**
10
10
  * Minimal JWT payload shape we read for cache scoping. The identity discriminator
@@ -57,11 +57,10 @@ export declare function fnv1a32(str: string): string;
57
57
  *
58
58
  * We use the decoded user id rather than the raw JWT so the token never lands
59
59
  * in a cache key (no token leakage through any cache-key logging, no key bloat).
60
- * The acting-as id is folded in because managed-account responses differ per
61
- * acting identity and `X-Acting-As` already changes the server response for
62
- * the same bearer token.
60
+ * Switching into a managed account mints a REAL new session whose access token
61
+ * carries the target account's id, so the identity tag changes naturally on a
62
+ * switch there is no separate acting-as discriminator to fold in.
63
63
  *
64
64
  * @param accessToken The current bearer access token, or `null` when anonymous.
65
- * @param actingAsUserId The active managed-account id, or `null`.
66
65
  */
67
- export declare function computeIdentityTag(accessToken: string | null, actingAsUserId: string | null): string;
66
+ export declare function computeIdentityTag(accessToken: string | null): string;
@@ -27,14 +27,6 @@ export declare function isIOS(): boolean;
27
27
  * Check if running on Android
28
28
  */
29
29
  export declare function isAndroid(): boolean;
30
- /**
31
- * Check if running in React Native
32
- */
33
- export declare function isReactNative(): boolean;
34
- /**
35
- * Check if running in Node.js
36
- */
37
- export declare function isNodeJS(): boolean;
38
30
  /**
39
31
  * Set the platform OS explicitly
40
32
  * Called by React Native entry point to register the platform
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxyhq/core",
3
- "version": "4.0.1",
3
+ "version": "5.1.0",
4
4
  "description": "OxyHQ SDK Foundation — API client, authentication, cryptographic identity, and shared utilities",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -43,10 +43,6 @@
43
43
  },
44
44
  "./package.json": "./package.json"
45
45
  },
46
- "react-native": {
47
- "./dist/esm/utils/platformCrypto.js": "./dist/esm/utils/platformCrypto.native.js",
48
- "./dist/cjs/utils/platformCrypto.js": "./dist/cjs/utils/platformCrypto.native.js"
49
- },
50
46
  "files": [
51
47
  "dist",
52
48
  "src"
@@ -98,14 +94,15 @@
98
94
  }
99
95
  },
100
96
  "dependencies": {
101
- "@oxyhq/contracts": "^0.6.0",
97
+ "@oxyhq/contracts": "^0.7.0",
98
+ "@oxyhq/protocol": "^0.1.1",
102
99
  "bip39": "^3.1.0",
103
100
  "buffer": "^6.0.3",
104
101
  "elliptic": "^6.6.1",
105
102
  "invariant": "^2.2.4",
106
103
  "jwt-decode": "^4.0.0",
107
104
  "socket.io-client": "^4.8.1",
108
- "tldts": "^7.0.22",
105
+ "tldts": "^7.4.5",
109
106
  "zod": "^3.25.64"
110
107
  },
111
108
  "peerDependencies": {
@@ -18,7 +18,8 @@ import { RequestDeduplicator, RequestQueue, SimpleLogger } from './utils/request
18
18
  import { retryAsync } from './utils/asyncUtils';
19
19
  import { handleHttpError } from './utils/errorUtils';
20
20
  import { jwtDecode } from 'jwt-decode';
21
- import { isNative, isReactNative, getPlatformOS } from './utils/platform';
21
+ import { isNative, getPlatformOS } from './utils/platform';
22
+ import { isReactNative } from '@oxyhq/protocol';
22
23
  import { computeIdentityTag, fnv1a32 } from './utils/cacheKey';
23
24
  import type { OxyConfig } from './models/interfaces';
24
25
 
@@ -238,9 +239,6 @@ export class HttpService {
238
239
  */
239
240
  private _tokenChangeListeners = new Set<(accessToken: string | null) => void>();
240
241
 
241
- // Acting-as identity for managed accounts
242
- private _actingAsUserId: string | null = null;
243
-
244
242
  // Performance monitoring
245
243
  private requestMetrics = {
246
244
  totalRequests: 0,
@@ -466,11 +464,6 @@ export class HttpService {
466
464
  });
467
465
  }
468
466
 
469
- // Add X-Acting-As header for managed account identity delegation
470
- if (this._actingAsUserId) {
471
- headers['X-Acting-As'] = this._actingAsUserId;
472
- }
473
-
474
467
  // Merge custom headers if provided
475
468
  if (config.headers) {
476
469
  Object.entries(config.headers).forEach(([key, value]) => {
@@ -820,12 +813,12 @@ export class HttpService {
820
813
  * Derive a stable, non-sensitive identity discriminator for cache scoping.
821
814
  *
822
815
  * Thin instance wrapper over the pure {@link computeIdentityTag} helper —
823
- * binds it to this instance's live access token and acting-as id. See that
824
- * function's docs for the full resolution contract (anon fallback, decoded
825
- * `userId || id`, token-hash fallback for undecodable tokens).
816
+ * binds it to this instance's live access token. See that function's docs for
817
+ * the full resolution contract (anon fallback, decoded `userId || id`,
818
+ * token-hash fallback for undecodable tokens).
826
819
  */
827
820
  private computeIdentityTag(): string {
828
- return computeIdentityTag(this.tokenStore.getAccessToken(), this._actingAsUserId);
821
+ return computeIdentityTag(this.tokenStore.getAccessToken());
829
822
  }
830
823
 
831
824
  /**
@@ -1107,15 +1100,6 @@ export class HttpService {
1107
1100
  return this.request<T>({ method: 'DELETE', url, ...config });
1108
1101
  }
1109
1102
 
1110
- // Acting-as identity management (managed accounts)
1111
- setActingAs(userId: string | null): void {
1112
- this._actingAsUserId = userId;
1113
- }
1114
-
1115
- getActingAs(): string | null {
1116
- return this._actingAsUserId;
1117
- }
1118
-
1119
1103
  // Token management
1120
1104
  setTokens(accessToken: string): void {
1121
1105
  this.tokenStore.setTokens(accessToken);
@@ -335,29 +335,6 @@ export class OxyServicesBase {
335
335
  }
336
336
  }
337
337
 
338
- /**
339
- * Set the acting-as identity for managed accounts.
340
- *
341
- * When set, all subsequent API requests will include the `X-Acting-As` header,
342
- * causing the server to attribute actions to the managed account. The
343
- * authenticated user must be an authorized manager of the target account.
344
- *
345
- * Pass `null` to clear and revert to the authenticated user's own identity.
346
- *
347
- * @param userId - The managed account user ID, or null to clear
348
- */
349
- public setActingAs(userId: string | null): void {
350
- this.httpService.setActingAs(userId);
351
- }
352
-
353
- /**
354
- * Get the current acting-as identity (managed account user ID), or null
355
- * if operating as the authenticated user's own identity.
356
- */
357
- public getActingAs(): string | null {
358
- return this.httpService.getActingAs();
359
- }
360
-
361
338
  /**
362
339
  * Wait for authentication to be ready
363
340
  *
@@ -112,25 +112,6 @@ describe('HttpService identity-scoped response cache', () => {
112
112
  expect(u2.profiles).toEqual(['for-user-2']);
113
113
  });
114
114
 
115
- it('partitions the cache by acting-as identity for the same bearer token', async () => {
116
- const http = newService();
117
- http.setTokens(makeJwt({ userId: 'owner-1' }));
118
-
119
- // Acting as managed account A.
120
- http.setActingAs('managed-a');
121
- fetchMock.mockResolvedValueOnce(jsonResponse({ items: ['a-only'] }));
122
- const a = await http.get<{ items: string[] }>('/some/managed-resource', { cache: true });
123
- expect(a.items).toEqual(['a-only']);
124
-
125
- // Acting as managed account B — different content, must miss the cache.
126
- http.setActingAs('managed-b');
127
- fetchMock.mockResolvedValueOnce(jsonResponse({ items: ['b-only'] }));
128
- const b = await http.get<{ items: string[] }>('/some/managed-resource', { cache: true });
129
-
130
- expect(fetchMock).toHaveBeenCalledTimes(2);
131
- expect(b.items).toEqual(['b-only']);
132
- });
133
-
134
115
  it('does NOT serve an authenticated cached response to an anonymous caller after clearTokens()', async () => {
135
116
  const http = newService();
136
117
  http.setTokens(makeJwt({ userId: 'user-1' }));
@@ -83,8 +83,9 @@ jest.mock(
83
83
  { virtual: true },
84
84
  );
85
85
 
86
- jest.mock('../../utils/platformCrypto', () => ({
86
+ jest.mock('@oxyhq/protocol', () => ({
87
87
  __esModule: true,
88
+ ...jest.requireActual('@oxyhq/protocol'),
88
89
  // eslint-disable-next-line @typescript-eslint/no-require-imports
89
90
  loadExpoCrypto: async () => require('expo-crypto'),
90
91
  // eslint-disable-next-line @typescript-eslint/no-require-imports
@@ -66,14 +66,16 @@ jest.mock(
66
66
  );
67
67
 
68
68
  // Production code routes platform-specific module loads through
69
- // `platformCrypto`, which ships in two physical variants on disk
70
- // (`platformCrypto.ts` / `platformCrypto.react-native.ts`) selected by the
71
- // consumer's bundler. Jest runs on Node — it picks the default variant,
72
- // which references Node's built-in `crypto`, not `expo-*`. For the test
73
- // suite to exercise the RN code paths, we mock the helper module to
74
- // delegate to the virtual `expo-*` modules registered above.
75
- jest.mock('../../utils/platformCrypto', () => ({
69
+ // `@oxyhq/protocol`'s `platform/crypto`, which ships in two physical variants
70
+ // on disk (`crypto.ts` / `crypto.native.ts`) selected by the consumer's
71
+ // bundler. Jest runs on Node — it picks the default variant, which references
72
+ // Node's built-in `crypto`, not `expo-*`. For the test suite to exercise the
73
+ // RN code paths, we override only the platform loaders to delegate to the
74
+ // virtual `expo-*` modules registered above, keeping every other protocol
75
+ // export (canonical bytes, signing, the platform predicates) real.
76
+ jest.mock('@oxyhq/protocol', () => ({
76
77
  __esModule: true,
78
+ ...jest.requireActual('@oxyhq/protocol'),
77
79
  loadExpoCrypto: async () => {
78
80
  // eslint-disable-next-line @typescript-eslint/no-require-imports
79
81
  return require('expo-crypto');
@@ -9,6 +9,7 @@
9
9
  */
10
10
 
11
11
  import { ec as EC } from 'elliptic';
12
+ import { verifySignature } from '@oxyhq/protocol';
12
13
  import { KeyManager } from '../keyManager';
13
14
  import { SignatureService } from '../signatureService';
14
15
 
@@ -40,7 +41,7 @@ describe('SignatureService.signChallengeWithSharedKey', () => {
40
41
  // uses, proving the format is unchanged and the shared key signed it.
41
42
  const message = `auth:${sharedPublicKey}:chal-123:${result.timestamp}`;
42
43
  await expect(
43
- SignatureService.verify(message, result.challenge, sharedPublicKey),
44
+ verifySignature(message, result.challenge, sharedPublicKey),
44
45
  ).resolves.toBe(true);
45
46
  });
46
47