@oxy.so/core 4.5.0-oxy-one.1 → 4.5.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 (282) hide show
  1. package/README.md +1 -1
  2. package/dist/cjs/HttpService.js +63 -19
  3. package/dist/cjs/api/accounts.js +10 -3
  4. package/dist/cjs/api/agency.js +3 -1
  5. package/dist/cjs/api/apps.js +15 -4
  6. package/dist/cjs/api/assets.js +39 -11
  7. package/dist/cjs/api/auth.js +28 -8
  8. package/dist/cjs/api/billing.js +24 -7
  9. package/dist/cjs/api/civic.js +14 -4
  10. package/dist/cjs/api/follows.js +9 -2
  11. package/dist/cjs/api/identity.js +31 -9
  12. package/dist/cjs/api/nodes.js +3 -1
  13. package/dist/cjs/api/privacy.js +8 -2
  14. package/dist/cjs/api/session.js +15 -4
  15. package/dist/cjs/api/store.js +15 -5
  16. package/dist/cjs/api/topics.js +10 -3
  17. package/dist/cjs/api/users.js +16 -4
  18. package/dist/cjs/boot/sessionColdBoot.js +40 -13
  19. package/dist/cjs/constants/version.js +6 -6
  20. package/dist/cjs/crypto/identityMarker.js +3 -1
  21. package/dist/cjs/crypto/identityProof.js +6 -1
  22. package/dist/cjs/crypto/keyManager.js +87 -32
  23. package/dist/cjs/crypto/recoveryPhrase.js +2 -2
  24. package/dist/cjs/crypto/signatureService.js +3 -4
  25. package/dist/cjs/i18n/index.js +16 -3
  26. package/dist/cjs/i18n/locales/ar-SA.js +366 -366
  27. package/dist/cjs/i18n/locales/ca-ES.js +366 -366
  28. package/dist/cjs/i18n/locales/de-DE.js +366 -366
  29. package/dist/cjs/i18n/locales/en-US.js +1994 -1994
  30. package/dist/cjs/i18n/locales/es-ES.js +1994 -1994
  31. package/dist/cjs/i18n/locales/fr-FR.js +366 -366
  32. package/dist/cjs/i18n/locales/it-IT.js +366 -366
  33. package/dist/cjs/i18n/locales/ja-JP.js +366 -366
  34. package/dist/cjs/i18n/locales/ko-KR.js +366 -366
  35. package/dist/cjs/i18n/locales/pt-PT.js +366 -366
  36. package/dist/cjs/i18n/locales/zh-CN.js +366 -366
  37. package/dist/cjs/inference/OxyInferenceClient.js +12 -18
  38. package/dist/cjs/models/interfaces.js +11 -11
  39. package/dist/cjs/server/OxyServer.js +86 -28
  40. package/dist/cjs/server/agentAccount.js +6 -2
  41. package/dist/cjs/server/aliaMachineCredential.js +39 -14
  42. package/dist/cjs/server/auth.js +5 -4
  43. package/dist/cjs/server/capabilityTicket.js +5 -5
  44. package/dist/cjs/server/index.js +2 -1
  45. package/dist/cjs/server/liveCapabilityTicket.js +4 -1
  46. package/dist/cjs/server/middleware.js +61 -42
  47. package/dist/cjs/server/namespaces.js +28 -7
  48. package/dist/cjs/server/requesterAssertion.js +36 -30
  49. package/dist/cjs/server/serviceTokenLifetime.js +7 -4
  50. package/dist/cjs/server/traffic.js +53 -14
  51. package/dist/cjs/server/trafficNodeHttp.js +33 -8
  52. package/dist/cjs/server/workloadIdentity.client.js +4 -0
  53. package/dist/cjs/server/workloadIdentity.js +28 -1
  54. package/dist/cjs/session/SessionClient.js +54 -17
  55. package/dist/cjs/session/accountDelegationAccess.js +4 -6
  56. package/dist/cjs/session/accountDialogController.js +1 -1
  57. package/dist/cjs/session/authStateStore.js +18 -9
  58. package/dist/cjs/session/identityPin.js +6 -2
  59. package/dist/cjs/session/refresh.js +81 -23
  60. package/dist/cjs/session/sharedDeviceCredential.js +28 -15
  61. package/dist/cjs/shared/utils/errorUtils.js +1 -1
  62. package/dist/cjs/shared/utils/networkUtils.js +1 -1
  63. package/dist/cjs/shared/utils/themeUtils.js +1 -1
  64. package/dist/cjs/utils/accountCacheSweep.js +3 -1
  65. package/dist/cjs/utils/accountUtils.js +1 -3
  66. package/dist/cjs/utils/asyncUtils.js +1 -1
  67. package/dist/cjs/utils/authHelpers.js +2 -1
  68. package/dist/cjs/utils/avatarUtils.js +4 -2
  69. package/dist/cjs/utils/cache.js +1 -1
  70. package/dist/cjs/utils/cacheKey.js +3 -1
  71. package/dist/cjs/utils/deviceManager.js +4 -4
  72. package/dist/cjs/utils/errorUtils.js +6 -4
  73. package/dist/cjs/utils/languageUtils.js +404 -57
  74. package/dist/cjs/utils/profileLinks.js +1 -3
  75. package/dist/cjs/utils/requestUtils.js +7 -1
  76. package/dist/cjs/utils/userIdentity.js +1 -1
  77. package/dist/cjs/utils/validationUtils.js +5 -6
  78. package/dist/esm/HttpService.js +64 -20
  79. package/dist/esm/api/accounts.js +11 -4
  80. package/dist/esm/api/agency.js +3 -1
  81. package/dist/esm/api/apps.js +15 -4
  82. package/dist/esm/api/assets.js +39 -11
  83. package/dist/esm/api/auth.js +28 -8
  84. package/dist/esm/api/billing.js +25 -8
  85. package/dist/esm/api/civic.js +14 -4
  86. package/dist/esm/api/follows.js +9 -2
  87. package/dist/esm/api/identity.js +31 -9
  88. package/dist/esm/api/nodes.js +3 -1
  89. package/dist/esm/api/privacy.js +8 -2
  90. package/dist/esm/api/session.js +15 -4
  91. package/dist/esm/api/store.js +15 -5
  92. package/dist/esm/api/topics.js +10 -3
  93. package/dist/esm/api/users.js +16 -4
  94. package/dist/esm/boot/sessionColdBoot.js +41 -14
  95. package/dist/esm/constants/version.js +6 -6
  96. package/dist/esm/crypto/identityMarker.js +3 -1
  97. package/dist/esm/crypto/identityProof.js +6 -1
  98. package/dist/esm/crypto/internal.js +1 -1
  99. package/dist/esm/crypto/keyManager.js +87 -32
  100. package/dist/esm/crypto/recoveryPhrase.js +2 -2
  101. package/dist/esm/crypto/signatureService.js +3 -4
  102. package/dist/esm/i18n/index.js +16 -3
  103. package/dist/esm/i18n/locales/ar-SA.js +366 -366
  104. package/dist/esm/i18n/locales/ca-ES.js +366 -366
  105. package/dist/esm/i18n/locales/de-DE.js +366 -366
  106. package/dist/esm/i18n/locales/en-US.js +1994 -1994
  107. package/dist/esm/i18n/locales/es-ES.js +1994 -1994
  108. package/dist/esm/i18n/locales/fr-FR.js +366 -366
  109. package/dist/esm/i18n/locales/it-IT.js +366 -366
  110. package/dist/esm/i18n/locales/ja-JP.js +366 -366
  111. package/dist/esm/i18n/locales/ko-KR.js +366 -366
  112. package/dist/esm/i18n/locales/pt-PT.js +366 -366
  113. package/dist/esm/i18n/locales/zh-CN.js +366 -366
  114. package/dist/esm/index.js +1 -1
  115. package/dist/esm/inference/OxyInferenceClient.js +13 -19
  116. package/dist/esm/models/interfaces.js +11 -11
  117. package/dist/esm/server/OxyServer.js +87 -29
  118. package/dist/esm/server/agentAccount.js +6 -2
  119. package/dist/esm/server/aliaMachineCredential.js +39 -14
  120. package/dist/esm/server/auth.js +6 -5
  121. package/dist/esm/server/capabilityTicket.js +6 -6
  122. package/dist/esm/server/index.js +5 -5
  123. package/dist/esm/server/liveCapabilityTicket.js +6 -3
  124. package/dist/esm/server/middleware.js +62 -43
  125. package/dist/esm/server/namespaces.js +31 -10
  126. package/dist/esm/server/requesterAssertion.js +36 -30
  127. package/dist/esm/server/serviceTokenLifetime.js +7 -4
  128. package/dist/esm/server/traffic.js +55 -16
  129. package/dist/esm/server/trafficNodeHttp.js +34 -9
  130. package/dist/esm/server/userInvalidation.js +1 -1
  131. package/dist/esm/server/workloadIdentity.client.js +3 -0
  132. package/dist/esm/server/workloadIdentity.js +27 -1
  133. package/dist/esm/session/SessionClient.js +54 -17
  134. package/dist/esm/session/accountDelegationAccess.js +5 -7
  135. package/dist/esm/session/accountDialogController.js +1 -1
  136. package/dist/esm/session/authStateStore.js +18 -9
  137. package/dist/esm/session/identityPin.js +6 -2
  138. package/dist/esm/session/refresh.js +82 -24
  139. package/dist/esm/session/sharedDeviceCredential.js +28 -15
  140. package/dist/esm/shared/utils/errorUtils.js +1 -1
  141. package/dist/esm/shared/utils/networkUtils.js +1 -1
  142. package/dist/esm/shared/utils/themeUtils.js +1 -1
  143. package/dist/esm/utils/accountCacheSweep.js +3 -1
  144. package/dist/esm/utils/accountUtils.js +1 -3
  145. package/dist/esm/utils/asyncUtils.js +1 -1
  146. package/dist/esm/utils/authHelpers.js +2 -1
  147. package/dist/esm/utils/avatarUtils.js +4 -2
  148. package/dist/esm/utils/cache.js +1 -1
  149. package/dist/esm/utils/cacheKey.js +3 -1
  150. package/dist/esm/utils/deviceManager.js +4 -4
  151. package/dist/esm/utils/errorUtils.js +6 -4
  152. package/dist/esm/utils/languageUtils.js +404 -57
  153. package/dist/esm/utils/profileLinks.js +1 -3
  154. package/dist/esm/utils/requestUtils.js +7 -1
  155. package/dist/esm/utils/userIdentity.js +1 -1
  156. package/dist/esm/utils/validationUtils.js +5 -6
  157. package/dist/types/HttpService.d.ts +1 -1
  158. package/dist/types/api/accounts.d.ts +1 -1
  159. package/dist/types/api/apps.d.ts +4 -4
  160. package/dist/types/crypto/internal.d.ts +1 -1
  161. package/dist/types/index.d.ts +4 -4
  162. package/dist/types/server/index.d.ts +6 -6
  163. package/dist/types/server/workloadIdentity.client.d.ts +5 -1
  164. package/dist/types/server/workloadIdentity.d.ts +26 -0
  165. package/dist/types/session/accountDelegationAccess.d.ts +1 -1
  166. package/dist/types/session/index.d.ts +1 -1
  167. package/dist/types/utils/userIdentity.d.ts +1 -1
  168. package/dist/types/utils/validationUtils.d.ts +4 -5
  169. package/package.json +2 -3
  170. package/src/HttpService.ts +183 -53
  171. package/src/OxyServices.errors.ts +7 -3
  172. package/src/OxyServices.ts +6 -2
  173. package/src/api/accounts.ts +76 -26
  174. package/src/api/agency.ts +24 -9
  175. package/src/api/appData.ts +12 -2
  176. package/src/api/apps.ts +81 -31
  177. package/src/api/assets.ts +113 -43
  178. package/src/api/auth.ts +145 -77
  179. package/src/api/billing.ts +112 -65
  180. package/src/api/civic.ts +61 -19
  181. package/src/api/contacts.ts +4 -1
  182. package/src/api/devices.ts +54 -21
  183. package/src/api/follows.ts +88 -31
  184. package/src/api/identity.ts +101 -33
  185. package/src/api/linkedAccounts.ts +26 -8
  186. package/src/api/nodes.ts +18 -6
  187. package/src/api/notifications.ts +26 -6
  188. package/src/api/privacy.ts +42 -8
  189. package/src/api/reputation.ts +18 -7
  190. package/src/api/session.ts +23 -5
  191. package/src/api/store.ts +81 -31
  192. package/src/api/topics.ts +18 -6
  193. package/src/api/users.ts +98 -35
  194. package/src/boot/sessionColdBoot.ts +60 -19
  195. package/src/civic/payloads.ts +1 -4
  196. package/src/client/context.ts +6 -1
  197. package/src/constants/version.ts +7 -6
  198. package/src/crypto/aead.ts +1 -5
  199. package/src/crypto/ecdh.ts +1 -4
  200. package/src/crypto/identityDerivations.ts +5 -1
  201. package/src/crypto/identityMarker.ts +3 -1
  202. package/src/crypto/identityProof.ts +6 -1
  203. package/src/crypto/index.ts +11 -11
  204. package/src/crypto/internal.ts +6 -1
  205. package/src/crypto/keyManager.ts +348 -98
  206. package/src/crypto/recoveryPhrase.ts +14 -9
  207. package/src/crypto/registrationPow.ts +1 -1
  208. package/src/crypto/signatureService.ts +14 -6
  209. package/src/i18n/accountCategoryLabels.ts +1 -4
  210. package/src/i18n/accountRoleLabels.ts +1 -4
  211. package/src/i18n/index.ts +25 -7
  212. package/src/i18n/locales/ar-SA.ts +375 -366
  213. package/src/i18n/locales/ca-ES.ts +382 -366
  214. package/src/i18n/locales/de-DE.ts +387 -366
  215. package/src/i18n/locales/en-US.ts +2034 -1994
  216. package/src/i18n/locales/es-ES.ts +2043 -1994
  217. package/src/i18n/locales/fr-FR.ts +387 -366
  218. package/src/i18n/locales/it-IT.ts +380 -366
  219. package/src/i18n/locales/ja-JP.ts +380 -366
  220. package/src/i18n/locales/ko-KR.ts +377 -366
  221. package/src/i18n/locales/pt-PT.ts +383 -366
  222. package/src/i18n/locales/zh-CN.ts +373 -366
  223. package/src/i18n/trustTierLabels.ts +1 -4
  224. package/src/index.ts +371 -349
  225. package/src/inference/OxyInferenceClient.ts +784 -780
  226. package/src/models/interfaces.ts +30 -27
  227. package/src/server/OxyServer.ts +227 -68
  228. package/src/server/agentAccount.ts +8 -2
  229. package/src/server/aliaMachineCredential.ts +57 -16
  230. package/src/server/auth.ts +9 -5
  231. package/src/server/capabilityTicket.ts +30 -21
  232. package/src/server/index.ts +29 -6
  233. package/src/server/liveCapabilityTicket.ts +31 -9
  234. package/src/server/middleware.ts +212 -121
  235. package/src/server/namespaces.ts +247 -94
  236. package/src/server/rateLimit.ts +2 -4
  237. package/src/server/requesterAssertion.ts +93 -54
  238. package/src/server/serviceTokenLifetime.ts +9 -4
  239. package/src/server/traffic.ts +117 -23
  240. package/src/server/trafficNodeHttp.ts +61 -14
  241. package/src/server/userInvalidation.ts +1 -4
  242. package/src/server/workloadIdentity.client.ts +12 -1
  243. package/src/server/workloadIdentity.ts +52 -5
  244. package/src/session/SessionClient.ts +170 -49
  245. package/src/session/accountDelegationAccess.ts +24 -31
  246. package/src/session/accountDialogController.ts +59 -14
  247. package/src/session/accountSwitchTargets.ts +10 -15
  248. package/src/session/authStateStore.ts +121 -81
  249. package/src/session/deviceDirectory.ts +1 -3
  250. package/src/session/identityPin.ts +10 -11
  251. package/src/session/identitySession.ts +3 -1
  252. package/src/session/index.ts +87 -82
  253. package/src/session/refresh.ts +138 -44
  254. package/src/session/sessionClientHost.ts +1 -3
  255. package/src/session/sharedDeviceCredential.ts +35 -14
  256. package/src/session/socketLoader.ts +9 -2
  257. package/src/shared/utils/errorUtils.ts +5 -2
  258. package/src/shared/utils/networkUtils.ts +5 -8
  259. package/src/shared/utils/themeUtils.ts +2 -6
  260. package/src/types/buffer.d.ts +66 -21
  261. package/src/types/expo-secure-store.d.ts +5 -1
  262. package/src/utils/accountCacheSweep.ts +4 -5
  263. package/src/utils/accountUtils.ts +107 -108
  264. package/src/utils/apiUtils.ts +3 -3
  265. package/src/utils/asyncUtils.ts +2 -2
  266. package/src/utils/authHelpers.ts +10 -5
  267. package/src/utils/avatarUtils.ts +11 -6
  268. package/src/utils/cache.ts +9 -10
  269. package/src/utils/cacheKey.ts +5 -1
  270. package/src/utils/coldBoot.ts +1 -3
  271. package/src/utils/commonsDelivery.ts +1 -4
  272. package/src/utils/deviceManager.ts +15 -14
  273. package/src/utils/errorUtils.ts +68 -86
  274. package/src/utils/languageUtils.ts +407 -58
  275. package/src/utils/oauthPkce.ts +1 -4
  276. package/src/utils/platform.ts +0 -1
  277. package/src/utils/profileLinks.ts +2 -5
  278. package/src/utils/requestUtils.ts +18 -14
  279. package/src/utils/sessionUtils.ts +34 -33
  280. package/src/utils/userHandle.ts +3 -1
  281. package/src/utils/userIdentity.ts +1 -1
  282. package/src/utils/validationUtils.ts +11 -9
@@ -225,7 +225,7 @@ export interface User {
225
225
  */
226
226
  themePreference?: ThemePreference;
227
227
  /** Product-specific personalization; never inferred from generic premium. */
228
- personalization?: {mentionMono:{allowed:boolean;expiresAt:string|null}};
228
+ personalization?: { mentionMono: { allowed: boolean; expiresAt: string | null } };
229
229
  /**
230
230
  * The authenticated viewer's relationship to THIS profile. Populated ONLY on
231
231
  * single-profile fetches (`getProfileByUsername` / `getUserById`) when the
@@ -340,12 +340,14 @@ export interface Transaction {
340
340
 
341
341
  export interface BlockedUser {
342
342
  _id?: string;
343
- blockedId: string | {
344
- _id: string;
345
- username: string;
346
- avatar?: string;
347
- name?: { displayName?: string };
348
- };
343
+ blockedId:
344
+ | string
345
+ | {
346
+ _id: string;
347
+ username: string;
348
+ avatar?: string;
349
+ name?: { displayName?: string };
350
+ };
349
351
  userId: string;
350
352
  createdAt?: string;
351
353
  blockedAt?: string;
@@ -355,12 +357,14 @@ export interface BlockedUser {
355
357
 
356
358
  export interface RestrictedUser {
357
359
  _id?: string;
358
- restrictedId: string | {
359
- _id: string;
360
- username: string;
361
- avatar?: string;
362
- name?: { displayName?: string };
363
- };
360
+ restrictedId:
361
+ | string
362
+ | {
363
+ _id: string;
364
+ username: string;
365
+ avatar?: string;
366
+ name?: { displayName?: string };
367
+ };
364
368
  userId: string;
365
369
  createdAt?: string;
366
370
  restrictedAt?: string;
@@ -792,7 +796,7 @@ export interface AccountStorageUsageResponse {
792
796
  /**
793
797
  * Security activity event types
794
798
  */
795
- export type SecurityEventType =
799
+ export type SecurityEventType =
796
800
  | 'sign_in'
797
801
  | 'sign_out'
798
802
  | 'email_changed'
@@ -815,17 +819,17 @@ export type SecurityEventSeverity = 'low' | 'medium' | 'high' | 'critical';
815
819
  * Maps each event type to its default severity level
816
820
  */
817
821
  export const SECURITY_EVENT_SEVERITY_MAP: Record<SecurityEventType, SecurityEventSeverity> = {
818
- 'sign_in': 'low',
819
- 'sign_out': 'low',
820
- 'profile_updated': 'low',
821
- 'email_changed': 'medium',
822
- 'device_added': 'medium',
823
- 'device_removed': 'medium',
824
- 'security_settings_changed': 'medium',
825
- 'account_recovery': 'high',
826
- 'private_key_exported': 'high',
827
- 'backup_created': 'high',
828
- 'suspicious_activity': 'critical',
822
+ sign_in: 'low',
823
+ sign_out: 'low',
824
+ profile_updated: 'low',
825
+ email_changed: 'medium',
826
+ device_added: 'medium',
827
+ device_removed: 'medium',
828
+ security_settings_changed: 'medium',
829
+ account_recovery: 'high',
830
+ private_key_exported: 'high',
831
+ backup_created: 'high',
832
+ suspicious_activity: 'critical',
829
833
  };
830
834
 
831
835
  /**
@@ -866,7 +870,7 @@ export interface AssetUploadProgress {
866
870
 
867
871
  // Device-linked session interfaces — the sessions that share one physical
868
872
  // device (GET /session/device/sessions/:sessionId). Distinct from the
869
- // server-authority `DeviceSession` Mongoose model / `DeviceSessionState`.
873
+ // server-authority `DeviceSession` record / `DeviceSessionState`.
870
874
  export interface DeviceLinkedSession {
871
875
  sessionId: string;
872
876
  deviceId: string;
@@ -899,4 +903,3 @@ export interface UpdateDeviceNameResponse {
899
903
  message: string;
900
904
  deviceName: string;
901
905
  }
902
-
@@ -1,4 +1,9 @@
1
- import { subjectProductAccessQuerySchema, subjectProductGrantSnapshotSchema, type SubjectProductAccessQuery, type SubjectProductGrantSnapshot } from '@oxy.so/contracts';
1
+ import {
2
+ subjectProductAccessQuerySchema,
3
+ subjectProductGrantSnapshotSchema,
4
+ type SubjectProductAccessQuery,
5
+ type SubjectProductGrantSnapshot,
6
+ } from '@oxy.so/contracts';
2
7
  /**
3
8
  * `OxyServer` — the Oxy client for a backend.
4
9
  *
@@ -33,9 +38,14 @@ import { hasBoundedServiceTokenLifetime } from './serviceTokenLifetime';
33
38
  function reusableServiceToken(token: string): boolean {
34
39
  try {
35
40
  const claims = jwtDecode<{ iat?: number; exp?: number }>(token);
36
- return hasBoundedServiceTokenLifetime(claims) && typeof claims.exp === 'number'
37
- && claims.exp > Math.floor(Date.now() / 1000);
38
- } catch { return false; }
41
+ return (
42
+ hasBoundedServiceTokenLifetime(claims) &&
43
+ typeof claims.exp === 'number' &&
44
+ claims.exp > Math.floor(Date.now() / 1000)
45
+ );
46
+ } catch {
47
+ return false;
48
+ }
39
49
  }
40
50
 
41
51
  import { loadNodeCrypto } from '@oxy.so/protocol';
@@ -128,7 +138,9 @@ export interface ServiceTokenResponse {
128
138
  */
129
139
  export class ServiceCredentialMismatchError extends Error {
130
140
  constructor() {
131
- super('Service credential mismatch: provided secret does not match the secret stored for this apiKey');
141
+ super(
142
+ 'Service credential mismatch: provided secret does not match the secret stored for this apiKey',
143
+ );
132
144
  this.name = 'ServiceCredentialMismatchError';
133
145
  }
134
146
  }
@@ -164,11 +176,18 @@ export class OxyServer extends OxyServices {
164
176
  private readonly serviceTokens = new Map<string, ServiceTokenCacheEntry>();
165
177
  private serviceApiKey: string | null = null;
166
178
  private serviceApiSecret: string | null = null;
167
- private readonly actingAs = new Map<string, { result: ServiceActingAsVerification | null; expiresAt: number }>();
179
+ private readonly actingAs = new Map<
180
+ string,
181
+ { result: ServiceActingAsVerification | null; expiresAt: number }
182
+ >();
168
183
  private readonly actingAsPending = new Map<string, Promise<ServiceActingAsVerification | null>>();
169
184
  private readonly actingAsGenerations = new Map<string, ActingAsGeneration>();
170
185
  /** Public keys only; never private material. */
171
- private readonly jwksCache: ServiceTokenJwksCache = { keys: new Map(), expiresAt: 0, lastAttemptAt: 0 };
186
+ private readonly jwksCache: ServiceTokenJwksCache = {
187
+ keys: new Map(),
188
+ expiresAt: 0,
189
+ lastAttemptAt: 0,
190
+ };
172
191
  /**
173
192
  * Before this instant a request without a user session does not try to mint
174
193
  * a service token and goes out anonymous. Set after a failed mint.
@@ -229,12 +248,14 @@ export class OxyServer extends OxyServices {
229
248
  }
230
249
  /** The inbox and push tokens, plus `create`. */
231
250
  override get notifications(): ServerNotificationsApi {
232
- if (!this._serverNotifications) this._serverNotifications = new ServerNotificationsApi(this.context);
251
+ if (!this._serverNotifications)
252
+ this._serverNotifications = new ServerNotificationsApi(this.context);
233
253
  return this._serverNotifications;
234
254
  }
235
255
  /** Linked external accounts, plus `forUser`. */
236
256
  override get linkedAccounts(): ServerLinkedAccountsApi {
237
- if (!this._serverLinkedAccounts) this._serverLinkedAccounts = new ServerLinkedAccountsApi(this.context);
257
+ if (!this._serverLinkedAccounts)
258
+ this._serverLinkedAccounts = new ServerLinkedAccountsApi(this.context);
238
259
  return this._serverLinkedAccounts;
239
260
  }
240
261
  /** Delegated capabilities, plus `introspectRequesterAssertion`. */
@@ -286,7 +307,9 @@ export class OxyServer extends OxyServices {
286
307
  if (canAttestWorkloadIdentity()) {
287
308
  return this.workloadServiceToken();
288
309
  }
289
- throw new Error('Service credentials not provided. Pass serviceAuth, call configureServiceAuth(), or pass apiKey and apiSecret.');
310
+ throw new Error(
311
+ 'Service credentials not provided. Pass serviceAuth, call configureServiceAuth(), or pass apiKey and apiSecret.',
312
+ );
290
313
  }
291
314
 
292
315
  // The apiKey is the credential's PUBLIC id (`oxy_dk_…`, the OAuth
@@ -312,7 +335,11 @@ export class OxyServer extends OxyServices {
312
335
  });
313
336
  throw new ServiceCredentialMismatchError();
314
337
  }
315
- if (entry.token && reusableServiceToken(entry.token) && entry.expiresAt > now + TOKEN_REUSE_MARGIN_MS) {
338
+ if (
339
+ entry.token &&
340
+ reusableServiceToken(entry.token) &&
341
+ entry.expiresAt > now + TOKEN_REUSE_MARGIN_MS
342
+ ) {
316
343
  return entry.token;
317
344
  }
318
345
  if (entry.pending) {
@@ -364,26 +391,43 @@ export class OxyServer extends OxyServices {
364
391
  * Never cached unless `cache` says so. Rejects with `OxyApiError`.
365
392
  */
366
393
  /** Validated request's user session only; never changes the shared client's token. */
367
- async productGrantSnapshotForUser(query: SubjectProductAccessQuery, accessToken: string): Promise<SubjectProductGrantSnapshot> {
394
+ async productGrantSnapshotForUser(
395
+ query: SubjectProductAccessQuery,
396
+ accessToken: string,
397
+ ): Promise<SubjectProductGrantSnapshot> {
368
398
  const parsed = subjectProductAccessQuerySchema.parse(query);
369
399
  if (!accessToken || /[\r\n]/.test(accessToken)) throw new Error('A user session is required');
370
- const answer = await this.request<unknown>('GET',
400
+ const answer = await this.request<unknown>(
401
+ 'GET',
371
402
  `/v1/products/${encodeURIComponent(parsed.productId)}/access/${encodeURIComponent(parsed.subjectAccountId)}/grants`,
372
- undefined, { cache: false, retry: false, headers: { Authorization: `Bearer ${accessToken}` } });
403
+ undefined,
404
+ { cache: false, retry: false, headers: { Authorization: `Bearer ${accessToken}` } },
405
+ );
373
406
  const snapshot = subjectProductGrantSnapshotSchema.parse(answer);
374
- if(snapshot.access.subjectAccountId !== parsed.subjectAccountId || snapshot.access.productId !== parsed.productId)
407
+ if (
408
+ snapshot.access.subjectAccountId !== parsed.subjectAccountId ||
409
+ snapshot.access.productId !== parsed.productId
410
+ )
375
411
  throw new Error('Product grant response attribution differs');
376
412
  return snapshot;
377
413
  }
378
414
 
379
415
  /** Offline metering read: the API requires this app's live consent for this exact subject. */
380
- async productGrantSnapshotForService(query: SubjectProductAccessQuery): Promise<SubjectProductGrantSnapshot> {
416
+ async productGrantSnapshotForService(
417
+ query: SubjectProductAccessQuery,
418
+ ): Promise<SubjectProductGrantSnapshot> {
381
419
  const parsed = subjectProductAccessQuerySchema.parse(query);
382
- const answer = await this.serviceRequest<unknown>('GET',
420
+ const answer = await this.serviceRequest<unknown>(
421
+ 'GET',
383
422
  `/v1/products/${encodeURIComponent(parsed.productId)}/access/${encodeURIComponent(parsed.subjectAccountId)}/service-grants`,
384
- undefined, { cache: false, retry: false });
423
+ undefined,
424
+ { cache: false, retry: false },
425
+ );
385
426
  const snapshot = subjectProductGrantSnapshotSchema.parse(answer);
386
- if (snapshot.access.subjectAccountId !== parsed.subjectAccountId || snapshot.access.productId !== parsed.productId)
427
+ if (
428
+ snapshot.access.subjectAccountId !== parsed.subjectAccountId ||
429
+ snapshot.access.productId !== parsed.productId
430
+ )
387
431
  throw new Error('Product grant response attribution differs');
388
432
  return snapshot;
389
433
  }
@@ -411,10 +455,18 @@ export class OxyServer extends OxyServices {
411
455
  * newer refusal even when app/credential revocation leaves epoch unchanged.
412
456
  */
413
457
  async verifyActingAs(
414
- appId: string, userId: string, options: ActingAsVerificationOptions = {},
458
+ appId: string,
459
+ userId: string,
460
+ options: ActingAsVerificationOptions = {},
415
461
  ): Promise<ServiceActingAsVerification | null> {
416
462
  const pair = JSON.stringify([appId, userId]);
417
- const cacheKey = JSON.stringify([appId, userId, options.credentialId, options.ownerAccountId, options.environment]);
463
+ const cacheKey = JSON.stringify([
464
+ appId,
465
+ userId,
466
+ options.credentialId,
467
+ options.ownerAccountId,
468
+ options.environment,
469
+ ]);
418
470
  if (options.cache === true) {
419
471
  const cached = this.actingAs.get(cacheKey);
420
472
  if (cached && cached.expiresAt > Date.now()) {
@@ -436,11 +488,21 @@ export class OxyServer extends OxyServices {
436
488
  }
437
489
  const serial = ++generation.issued;
438
490
  const startedAt = Date.now();
439
- const lookup = this.lookupActingAs(appId, userId, options, pair, cacheKey, generation, serial, startedAt);
491
+ const lookup = this.lookupActingAs(
492
+ appId,
493
+ userId,
494
+ options,
495
+ pair,
496
+ cacheKey,
497
+ generation,
498
+ serial,
499
+ startedAt,
500
+ );
440
501
  if (options.cache !== true) return lookup;
441
502
  this.actingAsPending.set(cacheKey, lookup);
442
- try { return await lookup; }
443
- finally {
503
+ try {
504
+ return await lookup;
505
+ } finally {
444
506
  if (this.actingAsPending.get(cacheKey) === lookup) this.actingAsPending.delete(cacheKey);
445
507
  }
446
508
  }
@@ -463,7 +525,9 @@ export class OxyServer extends OxyServices {
463
525
  * One page of account events for this application, oldest first — the pull
464
526
  * feed behind the webhook. Verify each entry's `token` before acting on it.
465
527
  */
466
- list: async (options: { after?: string; limit?: number } = {}): Promise<OxyAccountEventFeedPage> => {
528
+ list: async (
529
+ options: { after?: string; limit?: number } = {},
530
+ ): Promise<OxyAccountEventFeedPage> => {
467
531
  const query: Record<string, string> = {};
468
532
  if (options.after) query.after = options.after;
469
533
  if (options.limit !== undefined) query.limit = String(options.limit);
@@ -494,12 +558,15 @@ export class OxyServer extends OxyServices {
494
558
  return await this.serviceToken();
495
559
  } catch (error) {
496
560
  this.anonymousServiceTokenRetryAt = Date.now() + ANONYMOUS_SERVICE_TOKEN_RETRY_MS;
497
- logger.warn('[oxy.auth] No service token for a request without a session; sending it anonymous', {
498
- component: 'auth',
499
- method: 'serviceTokenForAnonymousRequest',
500
- retryInMs: ANONYMOUS_SERVICE_TOKEN_RETRY_MS,
501
- error: error instanceof Error ? error.message : String(error),
502
- });
561
+ logger.warn(
562
+ '[oxy.auth] No service token for a request without a session; sending it anonymous',
563
+ {
564
+ component: 'auth',
565
+ method: 'serviceTokenForAnonymousRequest',
566
+ retryInMs: ANONYMOUS_SERVICE_TOKEN_RETRY_MS,
567
+ error: error instanceof Error ? error.message : String(error),
568
+ },
569
+ );
503
570
  return null;
504
571
  }
505
572
  }
@@ -508,7 +575,12 @@ export class OxyServer extends OxyServices {
508
575
  return Boolean(this.serviceApiKey && this.serviceApiSecret) || canAttestWorkloadIdentity();
509
576
  }
510
577
 
511
- private async mintServiceToken(key: string, secret: string, cacheKey: string, secretBuf: Buffer): Promise<string> {
578
+ private async mintServiceToken(
579
+ key: string,
580
+ secret: string,
581
+ cacheKey: string,
582
+ secretBuf: Buffer,
583
+ ): Promise<string> {
512
584
  const response = await this.request<ServiceTokenResponse>(
513
585
  'POST',
514
586
  '/auth/service-token',
@@ -522,7 +594,13 @@ export class OxyServer extends OxyServices {
522
594
  entry.expiresAt = expiresAt;
523
595
  entry.secretBuf = secretBuf;
524
596
  } else {
525
- this.serviceTokens.set(cacheKey, { token: response.token, expiresAt, secretBuf, pending: null, apiKey: key });
597
+ this.serviceTokens.set(cacheKey, {
598
+ token: response.token,
599
+ expiresAt,
600
+ secretBuf,
601
+ pending: null,
602
+ apiKey: key,
603
+ });
526
604
  }
527
605
  return response.token;
528
606
  }
@@ -530,10 +608,21 @@ export class OxyServer extends OxyServices {
530
608
  /** A token obtained by attestation, cached like a credential's (one identity per process). */
531
609
  private async workloadServiceToken(): Promise<string> {
532
610
  const entry = this.serviceTokens.get(WORKLOAD_CACHE_KEY);
533
- if (entry?.token && reusableServiceToken(entry.token) && entry.expiresAt > Date.now() + TOKEN_REUSE_MARGIN_MS) return entry.token;
611
+ if (
612
+ entry?.token &&
613
+ reusableServiceToken(entry.token) &&
614
+ entry.expiresAt > Date.now() + TOKEN_REUSE_MARGIN_MS
615
+ )
616
+ return entry.token;
534
617
  if (entry?.pending) return entry.pending;
535
618
 
536
- const seeded = entry ?? { token: '', expiresAt: 0, secretBuf: Buffer.alloc(0), pending: null, apiKey: WORKLOAD_CACHE_KEY };
619
+ const seeded = entry ?? {
620
+ token: '',
621
+ expiresAt: 0,
622
+ secretBuf: Buffer.alloc(0),
623
+ pending: null,
624
+ apiKey: WORKLOAD_CACHE_KEY,
625
+ };
537
626
  this.serviceTokens.set(WORKLOAD_CACHE_KEY, seeded);
538
627
 
539
628
  const pending = (async () => {
@@ -551,7 +640,8 @@ export class OxyServer extends OxyServices {
551
640
  return await pending;
552
641
  } catch (error) {
553
642
  const failed = this.serviceTokens.get(WORKLOAD_CACHE_KEY);
554
- if (failed?.pending === pending && !failed.token) this.serviceTokens.delete(WORKLOAD_CACHE_KEY);
643
+ if (failed?.pending === pending && !failed.token)
644
+ this.serviceTokens.delete(WORKLOAD_CACHE_KEY);
555
645
  throw error;
556
646
  } finally {
557
647
  const settled = this.serviceTokens.get(WORKLOAD_CACHE_KEY);
@@ -560,20 +650,42 @@ export class OxyServer extends OxyServices {
560
650
  }
561
651
 
562
652
  private async lookupActingAs(
563
- appId: string, userId: string, options: ActingAsVerificationOptions,
564
- pair: string, cacheKey: string, generation: ActingAsGeneration, serial: number, startedAt: number,
653
+ appId: string,
654
+ userId: string,
655
+ options: ActingAsVerificationOptions,
656
+ pair: string,
657
+ cacheKey: string,
658
+ generation: ActingAsGeneration,
659
+ serial: number,
660
+ startedAt: number,
565
661
  ): Promise<ServiceActingAsVerification | null> {
566
662
  try {
567
663
  const serviceToken = await this.serviceToken();
568
664
  const result = await this.request<ServiceActingAsVerification>(
569
- 'GET', '/internal/service-acting-as/verify',
570
- { appId, userId, ...(options.credentialId ? { credentialId: options.credentialId,
571
- ownerAccountId: options.ownerAccountId, environment: options.environment } : {}) },
572
- { cache: false, retry: false, timeout: 5000, headers: { Authorization: `Bearer ${serviceToken}` } },
665
+ 'GET',
666
+ '/internal/service-acting-as/verify',
667
+ {
668
+ appId,
669
+ userId,
670
+ ...(options.credentialId
671
+ ? {
672
+ credentialId: options.credentialId,
673
+ ownerAccountId: options.ownerAccountId,
674
+ environment: options.environment,
675
+ }
676
+ : {}),
677
+ },
678
+ {
679
+ cache: false,
680
+ retry: false,
681
+ timeout: 5000,
682
+ headers: { Authorization: `Bearer ${serviceToken}` },
683
+ },
573
684
  );
574
685
  // A deadline bounds how long an already old response can authorize. It
575
686
  // is not by itself a measurement of end-to-end revocation latency.
576
- if (this.actingAsGenerations.get(pair) !== generation || serial < generation.completed) return null;
687
+ if (this.actingAsGenerations.get(pair) !== generation || serial < generation.completed)
688
+ return null;
577
689
  generation.completed = serial;
578
690
  this.clearActingAsPair(appId, userId);
579
691
  if (Date.now() - startedAt >= 5000) return null;
@@ -581,26 +693,45 @@ export class OxyServer extends OxyServices {
581
693
  if (typeof rawEpoch !== 'string' || !/^(?:0|[1-9][0-9]{0,18})$/.test(rawEpoch)) return null;
582
694
  const epoch = BigInt(rawEpoch);
583
695
  if (epoch > 9223372036854775807n) return null;
584
- if (typeof result.authorized !== 'boolean' || !Array.isArray(result.scopes)
585
- || !result.scopes.every((scope) => typeof scope === 'string' && scope.length > 0 && scope.trim() === scope)
586
- || new Set(result.scopes).size !== result.scopes.length
587
- || (!result.authorized && result.scopes.length !== 0)) return null;
696
+ if (
697
+ typeof result.authorized !== 'boolean' ||
698
+ !Array.isArray(result.scopes) ||
699
+ !result.scopes.every(
700
+ (scope) => typeof scope === 'string' && scope.length > 0 && scope.trim() === scope,
701
+ ) ||
702
+ new Set(result.scopes).size !== result.scopes.length ||
703
+ (!result.authorized && result.scopes.length !== 0)
704
+ )
705
+ return null;
588
706
  const older = epoch < generation.epoch;
589
707
  if (epoch > generation.epoch) generation.epoch = epoch;
590
708
  if (result.authorized && older) return null;
591
- const verified: ServiceActingAsVerification | null = result.authorized === true
592
- ? { authorized: true, scopes: result.scopes, epoch: rawEpoch } : null;
593
- if (options.cache === true) this.rememberActingAs(cacheKey, verified,
594
- startedAt + (verified ? ACTING_AS_GRANT_TTL_MS : ACTING_AS_DENIAL_TTL_MS));
709
+ const verified: ServiceActingAsVerification | null =
710
+ result.authorized === true
711
+ ? { authorized: true, scopes: result.scopes, epoch: rawEpoch }
712
+ : null;
713
+ if (options.cache === true)
714
+ this.rememberActingAs(
715
+ cacheKey,
716
+ verified,
717
+ startedAt + (verified ? ACTING_AS_GRANT_TTL_MS : ACTING_AS_DENIAL_TTL_MS),
718
+ );
595
719
  return verified;
596
720
  } catch (error) {
597
721
  if (this.actingAsGenerations.get(pair) === generation && serial >= generation.completed) {
598
722
  generation.completed = serial;
599
723
  this.clearActingAsPair(appId, userId);
600
724
  }
601
- logger.warn('[oxy.auth] verifyActingAs lookup failed — not cached', {
602
- component: 'auth', method: 'verifyActingAs', appId, userId,
603
- }, error);
725
+ logger.warn(
726
+ '[oxy.auth] verifyActingAs lookup failed — not cached',
727
+ {
728
+ component: 'auth',
729
+ method: 'verifyActingAs',
730
+ appId,
731
+ userId,
732
+ },
733
+ error,
734
+ );
604
735
  return null;
605
736
  }
606
737
  }
@@ -612,7 +743,11 @@ export class OxyServer extends OxyServices {
612
743
  }
613
744
  }
614
745
 
615
- private rememberActingAs(cacheKey: string, result: ServiceActingAsVerification | null, expiresAt: number): void {
746
+ private rememberActingAs(
747
+ cacheKey: string,
748
+ result: ServiceActingAsVerification | null,
749
+ expiresAt: number,
750
+ ): void {
616
751
  this.actingAs.delete(cacheKey);
617
752
  this.actingAs.set(cacheKey, { result, expiresAt });
618
753
  if (this.actingAs.size > ACTING_AS_CACHE_MAX) {
@@ -621,7 +756,10 @@ export class OxyServer extends OxyServices {
621
756
  }
622
757
  }
623
758
 
624
- private async verifyAccountEvent(token: string, options: VerifyAccountEventOptions): Promise<OxyAccountEvent> {
759
+ private async verifyAccountEvent(
760
+ token: string,
761
+ options: VerifyAccountEventOptions,
762
+ ): Promise<OxyAccountEvent> {
625
763
  if (typeof token !== 'string' || token.length === 0 || token.length > 16 * 1024) {
626
764
  throw new OxyAccountEventError('Account event token is missing or oversized');
627
765
  }
@@ -639,11 +777,11 @@ export class OxyServer extends OxyServices {
639
777
  throw new OxyAccountEventError('Account event token is malformed');
640
778
  }
641
779
  if (
642
- Object.keys(header).length !== 3
643
- || header.alg !== 'EdDSA'
644
- || header.typ !== 'secevent+jwt'
645
- || typeof header.kid !== 'string'
646
- || !/^[A-Za-z0-9._-]{1,128}$/.test(header.kid)
780
+ Object.keys(header).length !== 3 ||
781
+ header.alg !== 'EdDSA' ||
782
+ header.typ !== 'secevent+jwt' ||
783
+ typeof header.kid !== 'string' ||
784
+ !/^[A-Za-z0-9._-]{1,128}$/.test(header.kid)
647
785
  ) {
648
786
  throw new OxyAccountEventError('Account event token header is not supported');
649
787
  }
@@ -658,7 +796,9 @@ export class OxyServer extends OxyServices {
658
796
  );
659
797
  } catch (error) {
660
798
  throw new OxyAccountEventError(
661
- error instanceof Error ? error.message.replace('Service token', 'Account event') : 'Signing key is unavailable',
799
+ error instanceof Error
800
+ ? error.message.replace('Service token', 'Account event')
801
+ : 'Signing key is unavailable',
662
802
  );
663
803
  }
664
804
  const signature = Buffer.from(signatureB64, 'base64url');
@@ -667,8 +807,16 @@ export class OxyServer extends OxyServices {
667
807
  }
668
808
  let verified = false;
669
809
  try {
670
- const publicKey = nodeCrypto.createPublicKey({ key: jwk as unknown as import('node:crypto').JsonWebKey, format: 'jwk' });
671
- verified = nodeCrypto.verify(null, Buffer.from(`${headerB64}.${payloadB64}`), publicKey, signature);
810
+ const publicKey = nodeCrypto.createPublicKey({
811
+ key: jwk as unknown as import('node:crypto').JsonWebKey,
812
+ format: 'jwk',
813
+ });
814
+ verified = nodeCrypto.verify(
815
+ null,
816
+ Buffer.from(`${headerB64}.${payloadB64}`),
817
+ publicKey,
818
+ signature,
819
+ );
672
820
  } catch {
673
821
  verified = false;
674
822
  }
@@ -677,7 +825,7 @@ export class OxyServer extends OxyServices {
677
825
  if (payload.iss !== OXY_JWT_ISSUER) {
678
826
  throw new OxyAccountEventError('Account event token issuer is not Oxy');
679
827
  }
680
- const audience = options.audience ?? await this.configuredServiceAppId();
828
+ const audience = options.audience ?? (await this.configuredServiceAppId());
681
829
  if (typeof payload.aud !== 'string' || payload.aud !== audience) {
682
830
  throw new OxyAccountEventError('Account event token is addressed to another application');
683
831
  }
@@ -693,7 +841,12 @@ export class OxyServer extends OxyServices {
693
841
  }
694
842
  const entries = Object.entries(events as Record<string, unknown>);
695
843
  const deleted = (events as Record<string, unknown>)[OXY_ACCOUNT_DELETED_EVENT_URI];
696
- if (entries.length !== 1 || typeof deleted !== 'object' || deleted === null || Array.isArray(deleted)) {
844
+ if (
845
+ entries.length !== 1 ||
846
+ typeof deleted !== 'object' ||
847
+ deleted === null ||
848
+ Array.isArray(deleted)
849
+ ) {
697
850
  throw new OxyAccountEventError('Account event token carries an unknown event');
698
851
  }
699
852
  const body = deleted as Record<string, unknown>;
@@ -703,7 +856,11 @@ export class OxyServer extends OxyServices {
703
856
  if (typeof body.occurredAt !== 'string' || Number.isNaN(Date.parse(body.occurredAt))) {
704
857
  throw new OxyAccountEventError('Account event has no occurrence time');
705
858
  }
706
- if (body.username !== undefined && body.username !== null && typeof body.username !== 'string') {
859
+ if (
860
+ body.username !== undefined &&
861
+ body.username !== null &&
862
+ typeof body.username !== 'string'
863
+ ) {
707
864
  throw new OxyAccountEventError('Account event username is malformed');
708
865
  }
709
866
  return {
@@ -723,7 +880,9 @@ export class OxyServer extends OxyServices {
723
880
  const serviceToken = await this.serviceToken();
724
881
  const appId = jwtDecode<{ appId?: unknown }>(serviceToken).appId;
725
882
  if (typeof appId !== 'string' || appId.length === 0) {
726
- throw new OxyAccountEventError('No audience given and the service credential names no application');
883
+ throw new OxyAccountEventError(
884
+ 'No audience given and the service credential names no application',
885
+ );
727
886
  }
728
887
  return appId;
729
888
  }
@@ -19,8 +19,14 @@ export async function signInAgentAccount(options: {
19
19
  const publicKey = normalizeSecp256k1PublicKey(signer.publicKey.trim());
20
20
  const claims = await client.auth.agent.requestChallenge(publicKey);
21
21
  const timestamp = Date.now();
22
- if (claims.action !== 'agent_signin' || claims.accountId !== accountId || claims.actorId !== accountId
23
- || normalizeSecp256k1PublicKey(claims.publicKey) !== publicKey || !claims.authMethodId || claims.expiresAt <= timestamp) {
22
+ if (
23
+ claims.action !== 'agent_signin' ||
24
+ claims.accountId !== accountId ||
25
+ claims.actorId !== accountId ||
26
+ normalizeSecp256k1PublicKey(claims.publicKey) !== publicKey ||
27
+ !claims.authMethodId ||
28
+ claims.expiresAt <= timestamp
29
+ ) {
24
30
  throw new Error('Agent challenge does not match this account and signer');
25
31
  }
26
32
  const signature = await signer.signMessage(buildAgentProofMessage(claims, timestamp));