@oxy.so/core 4.5.0-oxy-one.1 → 4.6.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.
- package/README.md +1 -1
- package/dist/cjs/HttpService.js +63 -19
- package/dist/cjs/api/accounts.js +10 -3
- package/dist/cjs/api/agency.js +3 -1
- package/dist/cjs/api/apps.js +15 -4
- package/dist/cjs/api/assets.js +39 -11
- package/dist/cjs/api/auth.js +28 -8
- package/dist/cjs/api/billing.js +24 -7
- package/dist/cjs/api/civic.js +14 -4
- package/dist/cjs/api/follows.js +9 -2
- package/dist/cjs/api/identity.js +31 -9
- package/dist/cjs/api/nodes.js +3 -1
- package/dist/cjs/api/privacy.js +8 -2
- package/dist/cjs/api/session.js +15 -4
- package/dist/cjs/api/store.js +15 -5
- package/dist/cjs/api/topics.js +10 -3
- package/dist/cjs/api/users.js +16 -4
- package/dist/cjs/boot/sessionColdBoot.js +40 -13
- package/dist/cjs/constants/version.js +6 -6
- package/dist/cjs/crypto/identityMarker.js +3 -1
- package/dist/cjs/crypto/identityProof.js +6 -1
- package/dist/cjs/crypto/keyManager.js +87 -32
- package/dist/cjs/crypto/recoveryPhrase.js +2 -2
- package/dist/cjs/crypto/signatureService.js +3 -4
- package/dist/cjs/i18n/index.js +16 -3
- package/dist/cjs/i18n/locales/ar-SA.js +366 -366
- package/dist/cjs/i18n/locales/ca-ES.js +366 -366
- package/dist/cjs/i18n/locales/de-DE.js +366 -366
- package/dist/cjs/i18n/locales/en-US.js +1994 -1994
- package/dist/cjs/i18n/locales/es-ES.js +1994 -1994
- package/dist/cjs/i18n/locales/fr-FR.js +366 -366
- package/dist/cjs/i18n/locales/it-IT.js +366 -366
- package/dist/cjs/i18n/locales/ja-JP.js +366 -366
- package/dist/cjs/i18n/locales/ko-KR.js +366 -366
- package/dist/cjs/i18n/locales/pt-PT.js +366 -366
- package/dist/cjs/i18n/locales/zh-CN.js +366 -366
- package/dist/cjs/inference/OxyInferenceClient.js +12 -18
- package/dist/cjs/models/interfaces.js +11 -11
- package/dist/cjs/server/OxyServer.js +86 -28
- package/dist/cjs/server/agentAccount.js +6 -2
- package/dist/cjs/server/aliaMachineCredential.js +39 -14
- package/dist/cjs/server/auth.js +5 -4
- package/dist/cjs/server/capabilityTicket.js +5 -5
- package/dist/cjs/server/index.js +2 -1
- package/dist/cjs/server/liveCapabilityTicket.js +4 -1
- package/dist/cjs/server/middleware.js +61 -42
- package/dist/cjs/server/namespaces.js +28 -7
- package/dist/cjs/server/requesterAssertion.js +36 -30
- package/dist/cjs/server/serviceTokenLifetime.js +7 -4
- package/dist/cjs/server/traffic.js +53 -14
- package/dist/cjs/server/trafficNodeHttp.js +33 -8
- package/dist/cjs/server/workloadIdentity.client.js +4 -0
- package/dist/cjs/server/workloadIdentity.js +28 -1
- package/dist/cjs/session/SessionClient.js +54 -17
- package/dist/cjs/session/accountDelegationAccess.js +4 -6
- package/dist/cjs/session/accountDialogController.js +1 -1
- package/dist/cjs/session/authStateStore.js +18 -9
- package/dist/cjs/session/identityPin.js +6 -2
- package/dist/cjs/session/refresh.js +81 -23
- package/dist/cjs/session/sharedDeviceCredential.js +28 -15
- package/dist/cjs/shared/utils/errorUtils.js +1 -1
- package/dist/cjs/shared/utils/networkUtils.js +1 -1
- package/dist/cjs/shared/utils/themeUtils.js +1 -1
- package/dist/cjs/utils/accountCacheSweep.js +3 -1
- package/dist/cjs/utils/accountUtils.js +1 -3
- package/dist/cjs/utils/asyncUtils.js +1 -1
- package/dist/cjs/utils/authHelpers.js +2 -1
- package/dist/cjs/utils/avatarUtils.js +4 -2
- package/dist/cjs/utils/cache.js +1 -1
- package/dist/cjs/utils/cacheKey.js +3 -1
- package/dist/cjs/utils/deviceManager.js +4 -4
- package/dist/cjs/utils/errorUtils.js +6 -4
- package/dist/cjs/utils/languageUtils.js +404 -57
- package/dist/cjs/utils/profileLinks.js +1 -3
- package/dist/cjs/utils/requestUtils.js +7 -1
- package/dist/cjs/utils/userIdentity.js +1 -1
- package/dist/cjs/utils/validationUtils.js +5 -6
- package/dist/esm/HttpService.js +64 -20
- package/dist/esm/api/accounts.js +11 -4
- package/dist/esm/api/agency.js +3 -1
- package/dist/esm/api/apps.js +15 -4
- package/dist/esm/api/assets.js +39 -11
- package/dist/esm/api/auth.js +28 -8
- package/dist/esm/api/billing.js +25 -8
- package/dist/esm/api/civic.js +14 -4
- package/dist/esm/api/follows.js +9 -2
- package/dist/esm/api/identity.js +31 -9
- package/dist/esm/api/nodes.js +3 -1
- package/dist/esm/api/privacy.js +8 -2
- package/dist/esm/api/session.js +15 -4
- package/dist/esm/api/store.js +15 -5
- package/dist/esm/api/topics.js +10 -3
- package/dist/esm/api/users.js +16 -4
- package/dist/esm/boot/sessionColdBoot.js +41 -14
- package/dist/esm/constants/version.js +6 -6
- package/dist/esm/crypto/identityMarker.js +3 -1
- package/dist/esm/crypto/identityProof.js +6 -1
- package/dist/esm/crypto/internal.js +1 -1
- package/dist/esm/crypto/keyManager.js +87 -32
- package/dist/esm/crypto/recoveryPhrase.js +2 -2
- package/dist/esm/crypto/signatureService.js +3 -4
- package/dist/esm/i18n/index.js +16 -3
- package/dist/esm/i18n/locales/ar-SA.js +366 -366
- package/dist/esm/i18n/locales/ca-ES.js +366 -366
- package/dist/esm/i18n/locales/de-DE.js +366 -366
- package/dist/esm/i18n/locales/en-US.js +1994 -1994
- package/dist/esm/i18n/locales/es-ES.js +1994 -1994
- package/dist/esm/i18n/locales/fr-FR.js +366 -366
- package/dist/esm/i18n/locales/it-IT.js +366 -366
- package/dist/esm/i18n/locales/ja-JP.js +366 -366
- package/dist/esm/i18n/locales/ko-KR.js +366 -366
- package/dist/esm/i18n/locales/pt-PT.js +366 -366
- package/dist/esm/i18n/locales/zh-CN.js +366 -366
- package/dist/esm/index.js +1 -1
- package/dist/esm/inference/OxyInferenceClient.js +13 -19
- package/dist/esm/models/interfaces.js +11 -11
- package/dist/esm/server/OxyServer.js +87 -29
- package/dist/esm/server/agentAccount.js +6 -2
- package/dist/esm/server/aliaMachineCredential.js +39 -14
- package/dist/esm/server/auth.js +6 -5
- package/dist/esm/server/capabilityTicket.js +6 -6
- package/dist/esm/server/index.js +5 -5
- package/dist/esm/server/liveCapabilityTicket.js +6 -3
- package/dist/esm/server/middleware.js +62 -43
- package/dist/esm/server/namespaces.js +31 -10
- package/dist/esm/server/requesterAssertion.js +36 -30
- package/dist/esm/server/serviceTokenLifetime.js +7 -4
- package/dist/esm/server/traffic.js +55 -16
- package/dist/esm/server/trafficNodeHttp.js +34 -9
- package/dist/esm/server/userInvalidation.js +1 -1
- package/dist/esm/server/workloadIdentity.client.js +3 -0
- package/dist/esm/server/workloadIdentity.js +27 -1
- package/dist/esm/session/SessionClient.js +54 -17
- package/dist/esm/session/accountDelegationAccess.js +5 -7
- package/dist/esm/session/accountDialogController.js +1 -1
- package/dist/esm/session/authStateStore.js +18 -9
- package/dist/esm/session/identityPin.js +6 -2
- package/dist/esm/session/refresh.js +82 -24
- package/dist/esm/session/sharedDeviceCredential.js +28 -15
- package/dist/esm/shared/utils/errorUtils.js +1 -1
- package/dist/esm/shared/utils/networkUtils.js +1 -1
- package/dist/esm/shared/utils/themeUtils.js +1 -1
- package/dist/esm/utils/accountCacheSweep.js +3 -1
- package/dist/esm/utils/accountUtils.js +1 -3
- package/dist/esm/utils/asyncUtils.js +1 -1
- package/dist/esm/utils/authHelpers.js +2 -1
- package/dist/esm/utils/avatarUtils.js +4 -2
- package/dist/esm/utils/cache.js +1 -1
- package/dist/esm/utils/cacheKey.js +3 -1
- package/dist/esm/utils/deviceManager.js +4 -4
- package/dist/esm/utils/errorUtils.js +6 -4
- package/dist/esm/utils/languageUtils.js +404 -57
- package/dist/esm/utils/profileLinks.js +1 -3
- package/dist/esm/utils/requestUtils.js +7 -1
- package/dist/esm/utils/userIdentity.js +1 -1
- package/dist/esm/utils/validationUtils.js +5 -6
- package/dist/types/HttpService.d.ts +1 -1
- package/dist/types/api/accounts.d.ts +16 -3
- package/dist/types/api/apps.d.ts +4 -4
- package/dist/types/crypto/internal.d.ts +1 -1
- package/dist/types/index.d.ts +4 -4
- package/dist/types/models/interfaces.d.ts +9 -1
- package/dist/types/server/index.d.ts +6 -6
- package/dist/types/server/workloadIdentity.client.d.ts +5 -1
- package/dist/types/server/workloadIdentity.d.ts +26 -0
- package/dist/types/session/accountDelegationAccess.d.ts +1 -1
- package/dist/types/session/index.d.ts +1 -1
- package/dist/types/utils/userIdentity.d.ts +1 -1
- package/dist/types/utils/validationUtils.d.ts +4 -5
- package/package.json +3 -4
- package/src/HttpService.ts +183 -53
- package/src/OxyServices.errors.ts +7 -3
- package/src/OxyServices.ts +6 -2
- package/src/api/accounts.ts +96 -28
- package/src/api/agency.ts +24 -9
- package/src/api/appData.ts +12 -2
- package/src/api/apps.ts +81 -31
- package/src/api/assets.ts +113 -43
- package/src/api/auth.ts +145 -77
- package/src/api/billing.ts +112 -65
- package/src/api/civic.ts +61 -19
- package/src/api/contacts.ts +4 -1
- package/src/api/devices.ts +54 -21
- package/src/api/follows.ts +88 -31
- package/src/api/identity.ts +101 -33
- package/src/api/linkedAccounts.ts +26 -8
- package/src/api/nodes.ts +18 -6
- package/src/api/notifications.ts +26 -6
- package/src/api/privacy.ts +42 -8
- package/src/api/reputation.ts +18 -7
- package/src/api/session.ts +23 -5
- package/src/api/store.ts +81 -31
- package/src/api/topics.ts +18 -6
- package/src/api/users.ts +98 -35
- package/src/boot/sessionColdBoot.ts +60 -19
- package/src/civic/payloads.ts +1 -4
- package/src/client/context.ts +6 -1
- package/src/constants/version.ts +7 -6
- package/src/crypto/aead.ts +1 -5
- package/src/crypto/ecdh.ts +1 -4
- package/src/crypto/identityDerivations.ts +5 -1
- package/src/crypto/identityMarker.ts +3 -1
- package/src/crypto/identityProof.ts +6 -1
- package/src/crypto/index.ts +11 -11
- package/src/crypto/internal.ts +6 -1
- package/src/crypto/keyManager.ts +348 -98
- package/src/crypto/recoveryPhrase.ts +14 -9
- package/src/crypto/registrationPow.ts +1 -1
- package/src/crypto/signatureService.ts +14 -6
- package/src/i18n/accountCategoryLabels.ts +1 -4
- package/src/i18n/accountRoleLabels.ts +1 -4
- package/src/i18n/index.ts +25 -7
- package/src/i18n/locales/ar-SA.ts +375 -366
- package/src/i18n/locales/ca-ES.ts +382 -366
- package/src/i18n/locales/de-DE.ts +387 -366
- package/src/i18n/locales/en-US.ts +2034 -1994
- package/src/i18n/locales/es-ES.ts +2043 -1994
- package/src/i18n/locales/fr-FR.ts +387 -366
- package/src/i18n/locales/it-IT.ts +380 -366
- package/src/i18n/locales/ja-JP.ts +380 -366
- package/src/i18n/locales/ko-KR.ts +377 -366
- package/src/i18n/locales/pt-PT.ts +383 -366
- package/src/i18n/locales/zh-CN.ts +373 -366
- package/src/i18n/trustTierLabels.ts +1 -4
- package/src/index.ts +371 -349
- package/src/inference/OxyInferenceClient.ts +784 -780
- package/src/models/interfaces.ts +39 -27
- package/src/server/OxyServer.ts +227 -68
- package/src/server/agentAccount.ts +8 -2
- package/src/server/aliaMachineCredential.ts +57 -16
- package/src/server/auth.ts +9 -5
- package/src/server/capabilityTicket.ts +30 -21
- package/src/server/index.ts +29 -6
- package/src/server/liveCapabilityTicket.ts +31 -9
- package/src/server/middleware.ts +212 -121
- package/src/server/namespaces.ts +247 -94
- package/src/server/rateLimit.ts +2 -4
- package/src/server/requesterAssertion.ts +93 -54
- package/src/server/serviceTokenLifetime.ts +9 -4
- package/src/server/traffic.ts +117 -23
- package/src/server/trafficNodeHttp.ts +61 -14
- package/src/server/userInvalidation.ts +1 -4
- package/src/server/workloadIdentity.client.ts +12 -1
- package/src/server/workloadIdentity.ts +52 -5
- package/src/session/SessionClient.ts +170 -49
- package/src/session/accountDelegationAccess.ts +24 -31
- package/src/session/accountDialogController.ts +59 -14
- package/src/session/accountSwitchTargets.ts +10 -15
- package/src/session/authStateStore.ts +121 -81
- package/src/session/deviceDirectory.ts +1 -3
- package/src/session/identityPin.ts +10 -11
- package/src/session/identitySession.ts +3 -1
- package/src/session/index.ts +87 -82
- package/src/session/refresh.ts +138 -44
- package/src/session/sessionClientHost.ts +1 -3
- package/src/session/sharedDeviceCredential.ts +35 -14
- package/src/session/socketLoader.ts +9 -2
- package/src/shared/utils/errorUtils.ts +5 -2
- package/src/shared/utils/networkUtils.ts +5 -8
- package/src/shared/utils/themeUtils.ts +2 -6
- package/src/types/buffer.d.ts +66 -21
- package/src/types/expo-secure-store.d.ts +5 -1
- package/src/utils/accountCacheSweep.ts +4 -5
- package/src/utils/accountUtils.ts +107 -108
- package/src/utils/apiUtils.ts +3 -3
- package/src/utils/asyncUtils.ts +2 -2
- package/src/utils/authHelpers.ts +10 -5
- package/src/utils/avatarUtils.ts +11 -6
- package/src/utils/cache.ts +9 -10
- package/src/utils/cacheKey.ts +5 -1
- package/src/utils/coldBoot.ts +1 -3
- package/src/utils/commonsDelivery.ts +1 -4
- package/src/utils/deviceManager.ts +15 -14
- package/src/utils/errorUtils.ts +68 -86
- package/src/utils/languageUtils.ts +407 -58
- package/src/utils/oauthPkce.ts +1 -4
- package/src/utils/platform.ts +0 -1
- package/src/utils/profileLinks.ts +2 -5
- package/src/utils/requestUtils.ts +18 -14
- package/src/utils/sessionUtils.ts +34 -33
- package/src/utils/userHandle.ts +3 -1
- package/src/utils/userIdentity.ts +1 -1
- package/src/utils/validationUtils.ts +11 -9
package/src/models/interfaces.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
AccountKind,
|
|
3
3
|
AccountCategoryId,
|
|
4
|
+
AvatarCharacter,
|
|
4
5
|
UserNameResponse,
|
|
5
6
|
UserRelationship,
|
|
6
7
|
ThemePreference,
|
|
@@ -180,7 +181,15 @@ export interface User {
|
|
|
180
181
|
isFederated?: boolean;
|
|
181
182
|
/** Allow sharing this user's content on the fediverse. Defaults to true. */
|
|
182
183
|
fediverseSharing?: boolean;
|
|
184
|
+
/**
|
|
185
|
+
* Derived from `kind === 'bot'` (the SOLE definition), NOT from `type`.
|
|
186
|
+
*/
|
|
183
187
|
isAgent?: boolean;
|
|
188
|
+
/**
|
|
189
|
+
* The 3D avatar character of a `kind: 'bot'` account, drawn by Bloom's
|
|
190
|
+
* `Avatar`. Present ONLY on a bot; absent for every other kind.
|
|
191
|
+
*/
|
|
192
|
+
character?: AvatarCharacter;
|
|
184
193
|
isAutomated?: boolean;
|
|
185
194
|
instance?: string;
|
|
186
195
|
federation?: {
|
|
@@ -225,7 +234,7 @@ export interface User {
|
|
|
225
234
|
*/
|
|
226
235
|
themePreference?: ThemePreference;
|
|
227
236
|
/** Product-specific personalization; never inferred from generic premium. */
|
|
228
|
-
personalization?: {mentionMono:{allowed:boolean;expiresAt:string|null}};
|
|
237
|
+
personalization?: { mentionMono: { allowed: boolean; expiresAt: string | null } };
|
|
229
238
|
/**
|
|
230
239
|
* The authenticated viewer's relationship to THIS profile. Populated ONLY on
|
|
231
240
|
* single-profile fetches (`getProfileByUsername` / `getUserById`) when the
|
|
@@ -340,12 +349,14 @@ export interface Transaction {
|
|
|
340
349
|
|
|
341
350
|
export interface BlockedUser {
|
|
342
351
|
_id?: string;
|
|
343
|
-
blockedId:
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
352
|
+
blockedId:
|
|
353
|
+
| string
|
|
354
|
+
| {
|
|
355
|
+
_id: string;
|
|
356
|
+
username: string;
|
|
357
|
+
avatar?: string;
|
|
358
|
+
name?: { displayName?: string };
|
|
359
|
+
};
|
|
349
360
|
userId: string;
|
|
350
361
|
createdAt?: string;
|
|
351
362
|
blockedAt?: string;
|
|
@@ -355,12 +366,14 @@ export interface BlockedUser {
|
|
|
355
366
|
|
|
356
367
|
export interface RestrictedUser {
|
|
357
368
|
_id?: string;
|
|
358
|
-
restrictedId:
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
369
|
+
restrictedId:
|
|
370
|
+
| string
|
|
371
|
+
| {
|
|
372
|
+
_id: string;
|
|
373
|
+
username: string;
|
|
374
|
+
avatar?: string;
|
|
375
|
+
name?: { displayName?: string };
|
|
376
|
+
};
|
|
364
377
|
userId: string;
|
|
365
378
|
createdAt?: string;
|
|
366
379
|
restrictedAt?: string;
|
|
@@ -792,7 +805,7 @@ export interface AccountStorageUsageResponse {
|
|
|
792
805
|
/**
|
|
793
806
|
* Security activity event types
|
|
794
807
|
*/
|
|
795
|
-
export type SecurityEventType =
|
|
808
|
+
export type SecurityEventType =
|
|
796
809
|
| 'sign_in'
|
|
797
810
|
| 'sign_out'
|
|
798
811
|
| 'email_changed'
|
|
@@ -815,17 +828,17 @@ export type SecurityEventSeverity = 'low' | 'medium' | 'high' | 'critical';
|
|
|
815
828
|
* Maps each event type to its default severity level
|
|
816
829
|
*/
|
|
817
830
|
export const SECURITY_EVENT_SEVERITY_MAP: Record<SecurityEventType, SecurityEventSeverity> = {
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
831
|
+
sign_in: 'low',
|
|
832
|
+
sign_out: 'low',
|
|
833
|
+
profile_updated: 'low',
|
|
834
|
+
email_changed: 'medium',
|
|
835
|
+
device_added: 'medium',
|
|
836
|
+
device_removed: 'medium',
|
|
837
|
+
security_settings_changed: 'medium',
|
|
838
|
+
account_recovery: 'high',
|
|
839
|
+
private_key_exported: 'high',
|
|
840
|
+
backup_created: 'high',
|
|
841
|
+
suspicious_activity: 'critical',
|
|
829
842
|
};
|
|
830
843
|
|
|
831
844
|
/**
|
|
@@ -866,7 +879,7 @@ export interface AssetUploadProgress {
|
|
|
866
879
|
|
|
867
880
|
// Device-linked session interfaces — the sessions that share one physical
|
|
868
881
|
// device (GET /session/device/sessions/:sessionId). Distinct from the
|
|
869
|
-
// server-authority `DeviceSession`
|
|
882
|
+
// server-authority `DeviceSession` record / `DeviceSessionState`.
|
|
870
883
|
export interface DeviceLinkedSession {
|
|
871
884
|
sessionId: string;
|
|
872
885
|
deviceId: string;
|
|
@@ -899,4 +912,3 @@ export interface UpdateDeviceNameResponse {
|
|
|
899
912
|
message: string;
|
|
900
913
|
deviceName: string;
|
|
901
914
|
}
|
|
902
|
-
|
package/src/server/OxyServer.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
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
|
|
37
|
-
|
|
38
|
-
|
|
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(
|
|
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<
|
|
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 = {
|
|
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)
|
|
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)
|
|
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(
|
|
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 (
|
|
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(
|
|
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>(
|
|
400
|
+
const answer = await this.request<unknown>(
|
|
401
|
+
'GET',
|
|
371
402
|
`/v1/products/${encodeURIComponent(parsed.productId)}/access/${encodeURIComponent(parsed.subjectAccountId)}/grants`,
|
|
372
|
-
undefined,
|
|
403
|
+
undefined,
|
|
404
|
+
{ cache: false, retry: false, headers: { Authorization: `Bearer ${accessToken}` } },
|
|
405
|
+
);
|
|
373
406
|
const snapshot = subjectProductGrantSnapshotSchema.parse(answer);
|
|
374
|
-
if(
|
|
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(
|
|
416
|
+
async productGrantSnapshotForService(
|
|
417
|
+
query: SubjectProductAccessQuery,
|
|
418
|
+
): Promise<SubjectProductGrantSnapshot> {
|
|
381
419
|
const parsed = subjectProductAccessQuerySchema.parse(query);
|
|
382
|
-
const answer = await this.serviceRequest<unknown>(
|
|
420
|
+
const answer = await this.serviceRequest<unknown>(
|
|
421
|
+
'GET',
|
|
383
422
|
`/v1/products/${encodeURIComponent(parsed.productId)}/access/${encodeURIComponent(parsed.subjectAccountId)}/service-grants`,
|
|
384
|
-
undefined,
|
|
423
|
+
undefined,
|
|
424
|
+
{ cache: false, retry: false },
|
|
425
|
+
);
|
|
385
426
|
const snapshot = subjectProductGrantSnapshotSchema.parse(answer);
|
|
386
|
-
if (
|
|
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,
|
|
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([
|
|
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(
|
|
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 {
|
|
443
|
-
|
|
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 (
|
|
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(
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
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(
|
|
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, {
|
|
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 (
|
|
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 ?? {
|
|
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)
|
|
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,
|
|
564
|
-
|
|
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',
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
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)
|
|
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 (
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
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 =
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
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(
|
|
602
|
-
|
|
603
|
-
|
|
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(
|
|
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(
|
|
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
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
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
|
|
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({
|
|
671
|
-
|
|
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 (
|
|
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 (
|
|
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(
|
|
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 (
|
|
23
|
-
|
|
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));
|