@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.
- 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 +1 -1
- 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/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 +2 -3
- package/src/HttpService.ts +183 -53
- package/src/OxyServices.errors.ts +7 -3
- package/src/OxyServices.ts +6 -2
- package/src/api/accounts.ts +76 -26
- 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 +30 -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
|
@@ -37,21 +37,23 @@ const MAX_ASSERTION_BYTES = 4096;
|
|
|
37
37
|
|
|
38
38
|
const identifier = z.string().min(1).max(128);
|
|
39
39
|
|
|
40
|
-
export const oxyRequesterAssertionClaimsSchema = z
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
40
|
+
export const oxyRequesterAssertionClaimsSchema = z
|
|
41
|
+
.object({
|
|
42
|
+
iss: z.string().min(1).max(256),
|
|
43
|
+
aud: identifier,
|
|
44
|
+
/** The requester account. */
|
|
45
|
+
sub: identifier,
|
|
46
|
+
jti: z.string().uuid(),
|
|
47
|
+
iat: z.number().int().nonnegative(),
|
|
48
|
+
exp: z.number().int().positive(),
|
|
49
|
+
/** The application the assertion was minted for, and the only valid presenter. */
|
|
50
|
+
azp: identifier,
|
|
51
|
+
/** The exact credential of that application. */
|
|
52
|
+
cid: identifier,
|
|
53
|
+
/** The one native agent this requester may reach with it. */
|
|
54
|
+
agentId: identifier,
|
|
55
|
+
})
|
|
56
|
+
.strict();
|
|
55
57
|
|
|
56
58
|
export type OxyRequesterAssertionClaims = z.infer<typeof oxyRequesterAssertionClaimsSchema>;
|
|
57
59
|
|
|
@@ -99,7 +101,10 @@ export function signOxyRequesterAssertion(
|
|
|
99
101
|
signing: { readonly keyId: string; readonly privateKey: KeyObject },
|
|
100
102
|
): string {
|
|
101
103
|
const parsed = oxyRequesterAssertionClaimsSchema.parse(claims);
|
|
102
|
-
if (
|
|
104
|
+
if (
|
|
105
|
+
parsed.exp <= parsed.iat ||
|
|
106
|
+
parsed.exp - parsed.iat > OXY_REQUESTER_ASSERTION_MAX_TTL_SECONDS
|
|
107
|
+
) {
|
|
103
108
|
throw new OxyRequesterAssertionError('ttl_exceeded');
|
|
104
109
|
}
|
|
105
110
|
if (!KEY_ID_PATTERN.test(signing.keyId)) throw new OxyRequesterAssertionError('malformed');
|
|
@@ -120,11 +125,11 @@ export function readOxyRequesterAssertionKeyId(token: string): string {
|
|
|
120
125
|
}
|
|
121
126
|
const header = decodeObject(segments[0] as string);
|
|
122
127
|
if (
|
|
123
|
-
Object.keys(header).length !== 3
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
+
Object.keys(header).length !== 3 ||
|
|
129
|
+
header.alg !== ALGORITHM ||
|
|
130
|
+
header.typ !== OXY_REQUESTER_ASSERTION_TYPE ||
|
|
131
|
+
typeof header.kid !== 'string' ||
|
|
132
|
+
!KEY_ID_PATTERN.test(header.kid)
|
|
128
133
|
) {
|
|
129
134
|
throw new OxyRequesterAssertionError('malformed');
|
|
130
135
|
}
|
|
@@ -148,7 +153,11 @@ export function verifyOxyRequesterAssertion(
|
|
|
148
153
|
options: OxyRequesterAssertionVerificationOptions,
|
|
149
154
|
): OxyRequesterAssertionClaims {
|
|
150
155
|
readOxyRequesterAssertionKeyId(token);
|
|
151
|
-
const [encodedHeader, encodedPayload, encodedSignature] = token.split('.') as [
|
|
156
|
+
const [encodedHeader, encodedPayload, encodedSignature] = token.split('.') as [
|
|
157
|
+
string,
|
|
158
|
+
string,
|
|
159
|
+
string,
|
|
160
|
+
];
|
|
152
161
|
const publicKey = options.publicKey;
|
|
153
162
|
if (!publicKey || publicKey.asymmetricKeyType !== 'ed25519') {
|
|
154
163
|
throw new OxyRequesterAssertionError('unknown_key');
|
|
@@ -170,7 +179,10 @@ export function verifyOxyRequesterAssertion(
|
|
|
170
179
|
if (claims.aud !== options.audience) throw new OxyRequesterAssertionError('wrong_audience');
|
|
171
180
|
if (claims.iat > now + CLOCK_SKEW_SECONDS) throw new OxyRequesterAssertionError('not_yet_valid');
|
|
172
181
|
if (claims.exp <= now) throw new OxyRequesterAssertionError('expired');
|
|
173
|
-
if (
|
|
182
|
+
if (
|
|
183
|
+
claims.exp <= claims.iat ||
|
|
184
|
+
claims.exp - claims.iat > OXY_REQUESTER_ASSERTION_MAX_TTL_SECONDS
|
|
185
|
+
) {
|
|
174
186
|
throw new OxyRequesterAssertionError('ttl_exceeded');
|
|
175
187
|
}
|
|
176
188
|
return claims;
|
|
@@ -187,27 +199,35 @@ const JWKS_MAX_BYTES = 64 * 1024;
|
|
|
187
199
|
const JWKS_MAX_KEYS = 20;
|
|
188
200
|
|
|
189
201
|
function parseJwks(value: unknown): Map<string, KeyObject> {
|
|
190
|
-
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
202
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
203
|
+
throw new Error('JWKS is malformed');
|
|
191
204
|
const keys = (value as { keys?: unknown }).keys;
|
|
192
|
-
if (!Array.isArray(keys) || keys.length === 0 || keys.length > JWKS_MAX_KEYS)
|
|
205
|
+
if (!Array.isArray(keys) || keys.length === 0 || keys.length > JWKS_MAX_KEYS)
|
|
206
|
+
throw new Error('JWKS has an invalid key set');
|
|
193
207
|
const result = new Map<string, KeyObject>();
|
|
194
208
|
for (const entry of keys) {
|
|
195
|
-
if (typeof entry !== 'object' || entry === null || Array.isArray(entry))
|
|
209
|
+
if (typeof entry !== 'object' || entry === null || Array.isArray(entry))
|
|
210
|
+
throw new Error('JWKS key is malformed');
|
|
196
211
|
const key = entry as Record<string, unknown>;
|
|
197
212
|
if (
|
|
198
|
-
key.kty !== 'OKP'
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
)
|
|
213
|
+
key.kty !== 'OKP' ||
|
|
214
|
+
key.crv !== 'Ed25519' ||
|
|
215
|
+
key.alg !== 'EdDSA' ||
|
|
216
|
+
key.use !== 'sig' ||
|
|
217
|
+
typeof key.x !== 'string' ||
|
|
218
|
+
typeof key.kid !== 'string' ||
|
|
219
|
+
!KEY_ID_PATTERN.test(key.kid) ||
|
|
220
|
+
Object.prototype.hasOwnProperty.call(key, 'd') ||
|
|
221
|
+
result.has(key.kid)
|
|
222
|
+
)
|
|
223
|
+
throw new Error('JWKS key is unsupported');
|
|
208
224
|
const x = Buffer.from(key.x, 'base64url');
|
|
209
|
-
if (x.length !== 32 || x.toString('base64url') !== key.x)
|
|
210
|
-
|
|
225
|
+
if (x.length !== 32 || x.toString('base64url') !== key.x)
|
|
226
|
+
throw new Error('JWKS key is not Ed25519');
|
|
227
|
+
result.set(
|
|
228
|
+
key.kid,
|
|
229
|
+
createPublicKey({ key: { kty: 'OKP', crv: 'Ed25519', x: key.x }, format: 'jwk' }),
|
|
230
|
+
);
|
|
211
231
|
}
|
|
212
232
|
return result;
|
|
213
233
|
}
|
|
@@ -248,7 +268,8 @@ export function createOxyJwksKeyResolver(
|
|
|
248
268
|
});
|
|
249
269
|
if (!response.ok) throw new Error(`JWKS returned HTTP ${response.status}`);
|
|
250
270
|
const body = await response.text();
|
|
251
|
-
if (Buffer.byteLength(body, 'utf8') > JWKS_MAX_BYTES)
|
|
271
|
+
if (Buffer.byteLength(body, 'utf8') > JWKS_MAX_BYTES)
|
|
272
|
+
throw new Error('JWKS exceeds the size limit');
|
|
252
273
|
keys = parseJwks(JSON.parse(body) as unknown);
|
|
253
274
|
expiresAt = clock() + JWKS_CACHE_MS;
|
|
254
275
|
} finally {
|
|
@@ -346,13 +367,16 @@ function reject(
|
|
|
346
367
|
status,
|
|
347
368
|
applicationId: (req as OxyAuthRequest).serviceApp?.appId ?? null,
|
|
348
369
|
});
|
|
349
|
-
res
|
|
370
|
+
res
|
|
371
|
+
.status(status)
|
|
372
|
+
.json({ error, code, message: 'The requester assertion was not accepted', status });
|
|
350
373
|
}
|
|
351
374
|
|
|
352
375
|
function singleHeader(req: Request, name: string): string | null | 'invalid' {
|
|
353
376
|
const value = req.headers[name];
|
|
354
377
|
if (value === undefined) return null;
|
|
355
|
-
if (typeof value !== 'string' || value.length === 0 || value.length > MAX_ASSERTION_BYTES)
|
|
378
|
+
if (typeof value !== 'string' || value.length === 0 || value.length > MAX_ASSERTION_BYTES)
|
|
379
|
+
return 'invalid';
|
|
356
380
|
return value;
|
|
357
381
|
}
|
|
358
382
|
|
|
@@ -373,8 +397,9 @@ export function createOxyRequesterAssertionAuth(
|
|
|
373
397
|
let resolvePublicKey = options.resolvePublicKey;
|
|
374
398
|
const resolver = (): ((keyId: string) => Promise<KeyObject | undefined>) => {
|
|
375
399
|
resolvePublicKey ??= createOxyJwksKeyResolver({
|
|
376
|
-
jwksUrl:
|
|
377
|
-
|
|
400
|
+
jwksUrl:
|
|
401
|
+
options.jwksUrl ??
|
|
402
|
+
new URL('/capabilities/.well-known/jwks.json', introspector.baseURL).toString(),
|
|
378
403
|
});
|
|
379
404
|
return resolvePublicKey;
|
|
380
405
|
};
|
|
@@ -393,16 +418,23 @@ export function createOxyRequesterAssertionAuth(
|
|
|
393
418
|
const request = req as OxyRequesterAssertionRequest;
|
|
394
419
|
const serviceApp: OxyServiceAppContext | undefined = request.serviceApp;
|
|
395
420
|
if (!serviceApp) {
|
|
396
|
-
reject(
|
|
421
|
+
reject(
|
|
422
|
+
req,
|
|
423
|
+
res,
|
|
424
|
+
options,
|
|
425
|
+
401,
|
|
426
|
+
'REQUESTER_ASSERTION_REQUIRES_SERVICE_TOKEN',
|
|
427
|
+
'service_token_required',
|
|
428
|
+
);
|
|
397
429
|
return;
|
|
398
430
|
}
|
|
399
431
|
// One identity channel per request. An offline delegation header or an
|
|
400
432
|
// identity some earlier middleware attached must never be combined with, or
|
|
401
433
|
// silently replaced by, a present-requester assertion.
|
|
402
434
|
if (
|
|
403
|
-
req.headers['x-oxy-user-id'] !== undefined
|
|
404
|
-
|
|
405
|
-
|
|
435
|
+
req.headers['x-oxy-user-id'] !== undefined ||
|
|
436
|
+
request.serviceActingAs !== undefined ||
|
|
437
|
+
(request.userId !== undefined && request.userId !== null)
|
|
406
438
|
) {
|
|
407
439
|
reject(req, res, options, 400, 'REQUESTER_ASSERTION_CONFLICT', 'identity_conflict');
|
|
408
440
|
return;
|
|
@@ -437,17 +469,24 @@ export function createOxyRequesterAssertionAuth(
|
|
|
437
469
|
presenter: { applicationId: serviceApp.appId, credentialId: serviceApp.credentialId },
|
|
438
470
|
});
|
|
439
471
|
} catch {
|
|
440
|
-
reject(
|
|
472
|
+
reject(
|
|
473
|
+
req,
|
|
474
|
+
res,
|
|
475
|
+
options,
|
|
476
|
+
503,
|
|
477
|
+
'REQUESTER_ASSERTION_UNAVAILABLE',
|
|
478
|
+
'introspection_unavailable',
|
|
479
|
+
);
|
|
441
480
|
return;
|
|
442
481
|
}
|
|
443
482
|
|
|
444
483
|
if (
|
|
445
|
-
introspection.active !== true
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
484
|
+
introspection.active !== true ||
|
|
485
|
+
introspection.requesterAccountId !== claims.sub ||
|
|
486
|
+
introspection.agentId !== claims.agentId ||
|
|
487
|
+
introspection.applicationId !== claims.azp ||
|
|
488
|
+
introspection.credentialId !== claims.cid ||
|
|
489
|
+
introspection.jti !== claims.jti
|
|
451
490
|
) {
|
|
452
491
|
reject(req, res, options, 401, 'REQUESTER_ASSERTION_INACTIVE', 'inactive');
|
|
453
492
|
return;
|
|
@@ -6,8 +6,13 @@ export function hasBoundedServiceTokenLifetime(
|
|
|
6
6
|
claims: { iat?: unknown; exp?: unknown },
|
|
7
7
|
nowSeconds = Math.floor(Date.now() / 1000),
|
|
8
8
|
): boolean {
|
|
9
|
-
return
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
return (
|
|
10
|
+
typeof claims.iat === 'number' &&
|
|
11
|
+
Number.isSafeInteger(claims.iat) &&
|
|
12
|
+
typeof claims.exp === 'number' &&
|
|
13
|
+
Number.isSafeInteger(claims.exp) &&
|
|
14
|
+
claims.iat <= nowSeconds &&
|
|
15
|
+
claims.exp > claims.iat &&
|
|
16
|
+
claims.exp - claims.iat <= OXY_SERVICE_TOKEN_MAX_LIFETIME_SECONDS
|
|
17
|
+
);
|
|
13
18
|
}
|
package/src/server/traffic.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { observeNodeHttp, withoutNodeHttpObservation } from './trafficNodeHttp';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
observeTrafficSocket,
|
|
4
|
+
observeTrafficWebSocket,
|
|
5
|
+
type TrafficWebSocket,
|
|
6
|
+
type TrafficSocket,
|
|
7
|
+
} from '@oxy.so/telemetry/socket';
|
|
3
8
|
import { randomUUID } from 'node:crypto';
|
|
4
9
|
import type { RequestHandler } from 'express';
|
|
5
10
|
import { OxyServer } from './OxyServer';
|
|
@@ -9,7 +14,15 @@ import { OxyServer } from './OxyServer';
|
|
|
9
14
|
// server-only and the server barrel already exports `./workloadIdentity`, so
|
|
10
15
|
// there is nothing here for a phone to load.
|
|
11
16
|
import { canAttestWorkloadIdentity } from './workloadIdentity';
|
|
12
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
createTrafficCollector,
|
|
19
|
+
instrumentTrafficFetch,
|
|
20
|
+
trafficMiddleware,
|
|
21
|
+
type TrafficFlow,
|
|
22
|
+
type ServiceEndpoint,
|
|
23
|
+
resolveOxyServiceEndpoint,
|
|
24
|
+
infrastructureLocation,
|
|
25
|
+
} from '@oxy.so/telemetry/collector';
|
|
13
26
|
|
|
14
27
|
export interface EcosystemTrafficOptions {
|
|
15
28
|
service: string;
|
|
@@ -29,8 +42,14 @@ export function createEcosystemTraffic(options: EcosystemTrafficOptions): {
|
|
|
29
42
|
wrapFetch(fetcher: typeof fetch): typeof fetch;
|
|
30
43
|
installFetch(): void;
|
|
31
44
|
observeHttp: ReturnType<typeof trafficMiddleware>;
|
|
32
|
-
observeSocket(
|
|
33
|
-
|
|
45
|
+
observeSocket(
|
|
46
|
+
socket: TrafficSocket,
|
|
47
|
+
options?: Parameters<typeof observeTrafficSocket>[4],
|
|
48
|
+
): () => void;
|
|
49
|
+
observeWebSocket(
|
|
50
|
+
socket: TrafficWebSocket,
|
|
51
|
+
options?: Parameters<typeof observeTrafficWebSocket>[4],
|
|
52
|
+
): () => void;
|
|
34
53
|
stop(): Promise<void>;
|
|
35
54
|
} {
|
|
36
55
|
const region = options.region ?? process.env.AWS_REGION ?? 'unknown';
|
|
@@ -38,7 +57,9 @@ export function createEcosystemTraffic(options: EcosystemTrafficOptions): {
|
|
|
38
57
|
const activityKey = process.env.OXY_ACTIVITY_API_KEY;
|
|
39
58
|
const activitySecret = process.env.OXY_ACTIVITY_API_SECRET;
|
|
40
59
|
if (!options.credential && Boolean(activityKey) !== Boolean(activitySecret)) {
|
|
41
|
-
throw new Error(
|
|
60
|
+
throw new Error(
|
|
61
|
+
'Ecosystem activity requires a complete OXY_ACTIVITY_API_KEY and OXY_ACTIVITY_API_SECRET pair',
|
|
62
|
+
);
|
|
42
63
|
}
|
|
43
64
|
const apiKey = activityKey || process.env.OXY_SERVICE_API_KEY;
|
|
44
65
|
const apiSecret = activitySecret || process.env.OXY_SERVICE_API_SECRET;
|
|
@@ -73,24 +94,44 @@ export function createEcosystemTraffic(options: EcosystemTrafficOptions): {
|
|
|
73
94
|
const fetcher = globalThis.fetch.bind(globalThis);
|
|
74
95
|
const instanceId = randomUUID();
|
|
75
96
|
const location = options.location ?? infrastructureLocation(region);
|
|
76
|
-
if (!location)
|
|
97
|
+
if (!location)
|
|
98
|
+
throw new Error(
|
|
99
|
+
'Ecosystem activity requires a known infrastructure region or explicit location',
|
|
100
|
+
);
|
|
77
101
|
const post = async (path: string, data: unknown) => {
|
|
78
102
|
const token = await credential();
|
|
79
103
|
const response = await fetcher(new URL(path, baseURL), {
|
|
80
|
-
method: 'POST',
|
|
81
|
-
|
|
104
|
+
method: 'POST',
|
|
105
|
+
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
|
|
106
|
+
body: JSON.stringify(data),
|
|
107
|
+
signal: AbortSignal.timeout(4_000),
|
|
82
108
|
});
|
|
83
109
|
if (!response.ok) throw new Error('Activity collection failed');
|
|
84
110
|
};
|
|
85
|
-
const collector = createTrafficCollector(events => post('/internal/activity', events));
|
|
111
|
+
const collector = createTrafficCollector((events) => post('/internal/activity', events));
|
|
86
112
|
let stopped = false;
|
|
87
113
|
let heartbeatWork = Promise.resolve();
|
|
88
114
|
let heartbeatPending = false;
|
|
89
|
-
const reportError =
|
|
115
|
+
const reportError =
|
|
116
|
+
options.onError ?? (() => console.warn('Ecosystem activity publisher unavailable'));
|
|
90
117
|
const heartbeat = (removed = false) => {
|
|
91
118
|
if (!removed && (stopped || heartbeatPending)) return heartbeatWork;
|
|
92
119
|
heartbeatPending = true;
|
|
93
|
-
heartbeatWork = heartbeatWork
|
|
120
|
+
heartbeatWork = heartbeatWork
|
|
121
|
+
.then(() =>
|
|
122
|
+
post('/internal/activity/infrastructure', {
|
|
123
|
+
instanceId,
|
|
124
|
+
service: options.service,
|
|
125
|
+
region,
|
|
126
|
+
...location,
|
|
127
|
+
status: options.ready?.() ? 'online' : 'unknown',
|
|
128
|
+
removed,
|
|
129
|
+
}),
|
|
130
|
+
)
|
|
131
|
+
.catch(reportError)
|
|
132
|
+
.finally(() => {
|
|
133
|
+
heartbeatPending = false;
|
|
134
|
+
});
|
|
94
135
|
return heartbeatWork;
|
|
95
136
|
};
|
|
96
137
|
const flush = () => collector.flush().catch(reportError);
|
|
@@ -98,17 +139,30 @@ export function createEcosystemTraffic(options: EcosystemTrafficOptions): {
|
|
|
98
139
|
const sockets = new WeakMap<object, () => void>();
|
|
99
140
|
let fetchInstalled = false;
|
|
100
141
|
const wrapFetch = (fetcher: typeof fetch): typeof fetch => {
|
|
101
|
-
const wrapped = instrumentTrafficFetch(
|
|
102
|
-
|
|
142
|
+
const wrapped = instrumentTrafficFetch(
|
|
143
|
+
fetcher,
|
|
144
|
+
collector,
|
|
145
|
+
options.service,
|
|
146
|
+
region,
|
|
147
|
+
options.resolveEndpoint ?? resolveOxyServiceEndpoint,
|
|
148
|
+
);
|
|
149
|
+
return Object.assign(
|
|
150
|
+
(...args: Parameters<typeof fetch>) => withoutNodeHttpObservation(() => wrapped(...args)),
|
|
151
|
+
fetcher,
|
|
152
|
+
) as typeof fetch;
|
|
103
153
|
};
|
|
104
154
|
const observeHttp = trafficMiddleware(collector, options.service, region);
|
|
105
|
-
const timer = setInterval(() => {
|
|
155
|
+
const timer = setInterval(() => {
|
|
156
|
+
void flush();
|
|
157
|
+
}, 2_000);
|
|
106
158
|
timer.unref?.();
|
|
107
159
|
void heartbeat();
|
|
108
|
-
const heartbeatTimer = setInterval(() => {
|
|
160
|
+
const heartbeatTimer = setInterval(() => {
|
|
161
|
+
void heartbeat();
|
|
162
|
+
}, 10_000);
|
|
109
163
|
heartbeatTimer.unref?.();
|
|
110
164
|
return {
|
|
111
|
-
record: flow => collector.record({ ...flow, service: options.service, region }),
|
|
165
|
+
record: (flow) => collector.record({ ...flow, service: options.service, region }),
|
|
112
166
|
middleware: observeHttp as RequestHandler,
|
|
113
167
|
observeHttp,
|
|
114
168
|
wrapFetch,
|
|
@@ -118,14 +172,34 @@ export function createEcosystemTraffic(options: EcosystemTrafficOptions): {
|
|
|
118
172
|
const original = globalThis.fetch;
|
|
119
173
|
const wrapped = wrapFetch(original);
|
|
120
174
|
globalThis.fetch = wrapped;
|
|
121
|
-
cleanups.add(() => {
|
|
122
|
-
|
|
175
|
+
cleanups.add(() => {
|
|
176
|
+
if (globalThis.fetch === wrapped) globalThis.fetch = original;
|
|
177
|
+
});
|
|
178
|
+
cleanups.add(
|
|
179
|
+
observeNodeHttp(
|
|
180
|
+
collector,
|
|
181
|
+
options.service,
|
|
182
|
+
region,
|
|
183
|
+
options.resolveEndpoint ?? resolveOxyServiceEndpoint,
|
|
184
|
+
),
|
|
185
|
+
);
|
|
123
186
|
},
|
|
124
187
|
observeWebSocket(socket, socketOptions) {
|
|
125
188
|
const existing = sockets.get(socket);
|
|
126
189
|
if (existing) return existing;
|
|
127
|
-
const unobserve = observeTrafficWebSocket(
|
|
128
|
-
|
|
190
|
+
const unobserve = observeTrafficWebSocket(
|
|
191
|
+
socket,
|
|
192
|
+
collector,
|
|
193
|
+
options.service,
|
|
194
|
+
region,
|
|
195
|
+
socketOptions,
|
|
196
|
+
);
|
|
197
|
+
const cleanup = () => {
|
|
198
|
+
unobserve();
|
|
199
|
+
socket.off('close', cleanup);
|
|
200
|
+
cleanups.delete(cleanup);
|
|
201
|
+
sockets.delete(socket);
|
|
202
|
+
};
|
|
129
203
|
sockets.set(socket, cleanup);
|
|
130
204
|
socket.once('close', cleanup);
|
|
131
205
|
cleanups.add(cleanup);
|
|
@@ -134,13 +208,33 @@ export function createEcosystemTraffic(options: EcosystemTrafficOptions): {
|
|
|
134
208
|
observeSocket(socket, socketOptions) {
|
|
135
209
|
const existing = sockets.get(socket);
|
|
136
210
|
if (existing) return existing;
|
|
137
|
-
const unobserve = observeTrafficSocket(
|
|
138
|
-
|
|
211
|
+
const unobserve = observeTrafficSocket(
|
|
212
|
+
socket,
|
|
213
|
+
collector,
|
|
214
|
+
options.service,
|
|
215
|
+
region,
|
|
216
|
+
socketOptions,
|
|
217
|
+
);
|
|
218
|
+
const cleanup = () => {
|
|
219
|
+
unobserve();
|
|
220
|
+
socket.off('disconnect', cleanup);
|
|
221
|
+
cleanups.delete(cleanup);
|
|
222
|
+
sockets.delete(socket);
|
|
223
|
+
};
|
|
139
224
|
sockets.set(socket, cleanup);
|
|
140
225
|
socket.once('disconnect', cleanup);
|
|
141
226
|
cleanups.add(cleanup);
|
|
142
227
|
return cleanup;
|
|
143
228
|
},
|
|
144
|
-
async stop() {
|
|
229
|
+
async stop() {
|
|
230
|
+
if (stopped) return;
|
|
231
|
+
stopped = true;
|
|
232
|
+
for (const cleanup of cleanups) cleanup();
|
|
233
|
+
cleanups.clear();
|
|
234
|
+
clearInterval(timer);
|
|
235
|
+
clearInterval(heartbeatTimer);
|
|
236
|
+
await flush();
|
|
237
|
+
await heartbeat(true);
|
|
238
|
+
},
|
|
145
239
|
};
|
|
146
240
|
}
|
|
@@ -2,14 +2,23 @@ import http from 'node:http';
|
|
|
2
2
|
import https from 'node:https';
|
|
3
3
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
4
4
|
import { syncBuiltinESMExports } from 'node:module';
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
normalizeInfrastructureRegion,
|
|
7
|
+
trafficType,
|
|
8
|
+
type TrafficFlow,
|
|
9
|
+
type ServiceEndpoint,
|
|
10
|
+
} from '@oxy.so/telemetry/collector';
|
|
6
11
|
|
|
7
12
|
const suppressed = new AsyncLocalStorage<boolean>();
|
|
8
|
-
export function withoutNodeHttpObservation<T>(callback: () => T): T {
|
|
13
|
+
export function withoutNodeHttpObservation<T>(callback: () => T): T {
|
|
14
|
+
return suppressed.run(true, callback);
|
|
15
|
+
}
|
|
9
16
|
|
|
10
17
|
/** Node and Bun share the HTTP facade; Bun does not emit Node's HTTP diagnostic channels. */
|
|
11
18
|
export function observeNodeHttp(
|
|
12
|
-
collector: { record(flow: TrafficFlow): void },
|
|
19
|
+
collector: { record(flow: TrafficFlow): void },
|
|
20
|
+
service: string,
|
|
21
|
+
region: string,
|
|
13
22
|
resolveEndpoint: (url: URL) => ServiceEndpoint | undefined,
|
|
14
23
|
): () => void {
|
|
15
24
|
const seen = new WeakSet<http.ClientRequest>();
|
|
@@ -20,28 +29,60 @@ export function observeNodeHttp(
|
|
|
20
29
|
const host = request.getHeader('host') ?? request.host;
|
|
21
30
|
if (typeof host !== 'string') return request;
|
|
22
31
|
const url = new URL(request.path || '/', `${request.protocol || 'http:'}//${host}`);
|
|
23
|
-
if (
|
|
32
|
+
if (
|
|
33
|
+
/^\/(?:internal\/activity|auth\/service-token|platform-infrastructure)(?:\/|$)/.test(
|
|
34
|
+
url.pathname,
|
|
35
|
+
)
|
|
36
|
+
)
|
|
37
|
+
return request;
|
|
24
38
|
const peer = resolveEndpoint(url);
|
|
25
39
|
if (peer && !request.headersSent) request.setHeader('X-Oxy-Source-Region', region);
|
|
26
|
-
const flow: TrafficFlow = {
|
|
40
|
+
const flow: TrafficFlow = {
|
|
41
|
+
service,
|
|
42
|
+
region,
|
|
43
|
+
activityType: trafficType(url.pathname),
|
|
44
|
+
scope: peer ? 'internal' : 'external',
|
|
45
|
+
direction: 'outbound',
|
|
46
|
+
sourceRegion: region,
|
|
47
|
+
sourceService: service,
|
|
48
|
+
targetService: peer?.service,
|
|
49
|
+
};
|
|
27
50
|
const emit = request.emit;
|
|
28
51
|
let recorded = false;
|
|
29
|
-
const restore = () => {
|
|
52
|
+
const restore = () => {
|
|
53
|
+
if (request.emit === observedEmit) request.emit = emit;
|
|
54
|
+
restoreRequests.delete(restore);
|
|
55
|
+
};
|
|
30
56
|
// Intercept notifications without adding an 'error' listener: observation
|
|
31
57
|
// must not turn an unhandled application error into a swallowed error.
|
|
32
|
-
const observedEmit: typeof request.emit = function (
|
|
58
|
+
const observedEmit: typeof request.emit = function (
|
|
59
|
+
this: http.ClientRequest,
|
|
60
|
+
event: string | symbol,
|
|
61
|
+
...args: unknown[]
|
|
62
|
+
) {
|
|
33
63
|
try {
|
|
34
64
|
if (!recorded && event === 'response') {
|
|
35
65
|
recorded = true;
|
|
36
66
|
const response = args[0] as http.IncomingMessage;
|
|
37
|
-
const targetRegion = peer
|
|
67
|
+
const targetRegion = peer
|
|
68
|
+
? normalizeInfrastructureRegion(response.headers['x-oxy-region'])
|
|
69
|
+
: undefined;
|
|
38
70
|
collector.record({ ...flow, targetRegion });
|
|
39
|
-
collector.record({
|
|
71
|
+
collector.record({
|
|
72
|
+
...flow,
|
|
73
|
+
direction: 'inbound',
|
|
74
|
+
sourceRegion: targetRegion,
|
|
75
|
+
targetRegion: region,
|
|
76
|
+
sourceService: peer?.service,
|
|
77
|
+
targetService: service,
|
|
78
|
+
});
|
|
40
79
|
} else if (!recorded && (event === 'error' || event === 'close')) {
|
|
41
80
|
recorded = true;
|
|
42
81
|
collector.record(flow);
|
|
43
82
|
}
|
|
44
|
-
} catch {
|
|
83
|
+
} catch {
|
|
84
|
+
/* A metrics failure cannot change a request's behavior. */
|
|
85
|
+
}
|
|
45
86
|
if (event === 'close') restore();
|
|
46
87
|
return emit.call(this, event, ...args);
|
|
47
88
|
};
|
|
@@ -50,13 +91,19 @@ export function observeNodeHttp(
|
|
|
50
91
|
return request;
|
|
51
92
|
};
|
|
52
93
|
const safelyObserve = (request: http.ClientRequest) => {
|
|
53
|
-
try {
|
|
94
|
+
try {
|
|
95
|
+
return observe(request);
|
|
96
|
+
} catch {
|
|
97
|
+
return request;
|
|
98
|
+
}
|
|
54
99
|
};
|
|
55
|
-
const patches = [http, https].map(module => {
|
|
100
|
+
const patches = [http, https].map((module) => {
|
|
56
101
|
const request = module.request;
|
|
57
102
|
const get = module.get;
|
|
58
|
-
const wrappedRequest = ((...args: Parameters<typeof request>) =>
|
|
59
|
-
|
|
103
|
+
const wrappedRequest = ((...args: Parameters<typeof request>) =>
|
|
104
|
+
safelyObserve(request.apply(module, args))) as typeof request;
|
|
105
|
+
const wrappedGet = ((...args: Parameters<typeof get>) =>
|
|
106
|
+
safelyObserve(get.apply(module, args))) as typeof get;
|
|
60
107
|
module.request = wrappedRequest;
|
|
61
108
|
module.get = wrappedGet;
|
|
62
109
|
return () => {
|
|
@@ -58,10 +58,7 @@ import {
|
|
|
58
58
|
type OxyUserChangeReason,
|
|
59
59
|
type OxyUserInvalidationEvent,
|
|
60
60
|
} from '@oxy.so/contracts';
|
|
61
|
-
import {
|
|
62
|
-
evictOxyIdentityCache,
|
|
63
|
-
type OxyIdentityCacheEvictor,
|
|
64
|
-
} from '../utils/identityCacheSweep';
|
|
61
|
+
import { evictOxyIdentityCache, type OxyIdentityCacheEvictor } from '../utils/identityCacheSweep';
|
|
65
62
|
|
|
66
63
|
/**
|
|
67
64
|
* The publish surface of a Redis client. Both `ioredis` and `node-redis`
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
WorkloadAttestation,
|
|
3
|
+
WorkloadServiceToken,
|
|
4
|
+
WorkloadServiceTokenOptions,
|
|
5
|
+
} from './workloadIdentity';
|
|
2
6
|
|
|
3
7
|
/** Workload attestation belongs to a Node host, never a browser or device. */
|
|
4
8
|
export function canAttestWorkloadIdentity(): boolean {
|
|
@@ -10,3 +14,10 @@ export async function requestWorkloadServiceToken(
|
|
|
10
14
|
): Promise<WorkloadServiceToken> {
|
|
11
15
|
throw new Error('Workload identity is only available on a Node host');
|
|
12
16
|
}
|
|
17
|
+
|
|
18
|
+
export async function attestWorkloadIdentity(_options: {
|
|
19
|
+
nonce: string;
|
|
20
|
+
fetch?: typeof fetch;
|
|
21
|
+
}): Promise<WorkloadAttestation> {
|
|
22
|
+
throw new Error('Workload identity is only available on a Node host');
|
|
23
|
+
}
|