@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
|
@@ -31,30 +31,53 @@ export interface OxyAliaMachineRequest extends Request {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
function id(value: unknown): value is string {
|
|
34
|
-
return
|
|
34
|
+
return (
|
|
35
|
+
typeof value === 'string' && value.length > 0 && value.length <= 128 && value.trim() === value
|
|
36
|
+
);
|
|
35
37
|
}
|
|
36
38
|
|
|
37
39
|
/** Strict response boundary: extra user/tier fields cannot smuggle authority. */
|
|
38
40
|
export function isOxyAliaMachinePrincipal(value: unknown): value is OxyAliaMachinePrincipal {
|
|
39
41
|
if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
|
|
40
42
|
const p = value as Record<string, unknown>;
|
|
41
|
-
const keys = [
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
43
|
+
const keys = [
|
|
44
|
+
'kind',
|
|
45
|
+
'audience',
|
|
46
|
+
'applicationId',
|
|
47
|
+
'credentialId',
|
|
48
|
+
'ownerAccountId',
|
|
49
|
+
'environment',
|
|
50
|
+
'scopes',
|
|
51
|
+
];
|
|
52
|
+
return (
|
|
53
|
+
Object.keys(p).length === keys.length &&
|
|
54
|
+
keys.every((key) => Object.prototype.hasOwnProperty.call(p, key)) &&
|
|
55
|
+
p.kind === 'machine' &&
|
|
56
|
+
p.audience === OXY_ALIA_RESOURCE_APPLICATION_ID &&
|
|
57
|
+
id(p.applicationId) &&
|
|
58
|
+
id(p.credentialId) &&
|
|
59
|
+
id(p.ownerAccountId) &&
|
|
60
|
+
typeof p.environment === 'string' &&
|
|
61
|
+
['production', 'staging', 'development'].includes(p.environment) &&
|
|
62
|
+
Array.isArray(p.scopes) &&
|
|
63
|
+
p.scopes.length === 2 &&
|
|
64
|
+
OXY_ALIA_MACHINE_SCOPES.every((scope) => (p.scopes as unknown[]).includes(scope))
|
|
65
|
+
);
|
|
48
66
|
}
|
|
49
67
|
|
|
50
68
|
// Keep the forwarding credential out of enumerable request/principal objects,
|
|
51
69
|
// logs, response serialization and persistent SDK/session state. No cache.
|
|
52
|
-
const forwardingCredentials = new WeakMap<
|
|
70
|
+
const forwardingCredentials = new WeakMap<
|
|
71
|
+
Request,
|
|
72
|
+
{ principal: OxyAliaMachinePrincipal; bearer: string }
|
|
73
|
+
>();
|
|
53
74
|
|
|
54
75
|
/** Only for the request-scoped Oxy inference client; never for a user API. */
|
|
55
76
|
export function getOxyAliaMachineCredentialBearer(req: Request): string | null {
|
|
56
77
|
const bound = forwardingCredentials.get(req);
|
|
57
|
-
return bound && (req as OxyAliaMachineRequest).machineCredential === bound.principal
|
|
78
|
+
return bound && (req as OxyAliaMachineRequest).machineCredential === bound.principal
|
|
79
|
+
? bound.bearer
|
|
80
|
+
: null;
|
|
58
81
|
}
|
|
59
82
|
|
|
60
83
|
/** Opt into Alia's app-only chat lane on its two chat routes, never globally. */
|
|
@@ -69,20 +92,38 @@ export function createOxyAliaMachineCredentialAuth(
|
|
|
69
92
|
res.status(401).json({ error: 'MACHINE_CREDENTIAL_INVALID' });
|
|
70
93
|
return;
|
|
71
94
|
}
|
|
72
|
-
const existing = req as Request & {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
95
|
+
const existing = req as Request & {
|
|
96
|
+
user?: unknown;
|
|
97
|
+
userId?: unknown;
|
|
98
|
+
serviceApp?: unknown;
|
|
99
|
+
oxyRequester?: unknown;
|
|
100
|
+
};
|
|
101
|
+
if (
|
|
102
|
+
existing.user ||
|
|
103
|
+
existing.userId ||
|
|
104
|
+
existing.serviceApp ||
|
|
105
|
+
existing.oxyRequester ||
|
|
106
|
+
(req as OxyAliaMachineRequest).machineCredential ||
|
|
107
|
+
req.headers['x-oxy-user-id'] !== undefined ||
|
|
108
|
+
req.headers['x-oxy-requester-assertion'] !== undefined
|
|
109
|
+
) {
|
|
76
110
|
res.status(403).json({ error: 'MACHINE_USER_DELEGATION_UNSUPPORTED' });
|
|
77
111
|
return;
|
|
78
112
|
}
|
|
79
113
|
try {
|
|
80
114
|
const answer = await server.apps.introspectAliaMachineCredential(token);
|
|
81
|
-
if (
|
|
115
|
+
if (
|
|
116
|
+
!answer.active ||
|
|
117
|
+
!isOxyAliaMachinePrincipal(answer.principal) ||
|
|
118
|
+
answer.principal.environment !== options.environment
|
|
119
|
+
) {
|
|
82
120
|
res.status(401).json({ error: 'MACHINE_CREDENTIAL_INVALID' });
|
|
83
121
|
return;
|
|
84
122
|
}
|
|
85
|
-
const principal = Object.freeze({
|
|
123
|
+
const principal = Object.freeze({
|
|
124
|
+
...answer.principal,
|
|
125
|
+
scopes: Object.freeze(['alia:chat', 'inference:invoke'] as const),
|
|
126
|
+
});
|
|
86
127
|
(req as OxyAliaMachineRequest).machineCredential = principal;
|
|
87
128
|
forwardingCredentials.set(req, { principal, bearer: token });
|
|
88
129
|
next();
|
package/src/server/auth.ts
CHANGED
|
@@ -2,7 +2,10 @@ import type { NextFunction, Request, RequestHandler, Response } from 'express';
|
|
|
2
2
|
import type { AuthMiddlewareOptions, OxyAuthRefusal, OxyMiddleware } from './middleware';
|
|
3
3
|
import type { AccountActorChain } from '@oxy.so/contracts';
|
|
4
4
|
import { logger } from '../logger';
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
OXY_SERVICE_ENVIRONMENTS,
|
|
7
|
+
type OxyServiceEnvironment,
|
|
8
|
+
} from '../utils/oxyServiceEnvironment';
|
|
6
9
|
|
|
7
10
|
export { OXY_SERVICE_ENVIRONMENTS };
|
|
8
11
|
export type { OxyServiceEnvironment, OxyAuthRefusal };
|
|
@@ -96,9 +99,7 @@ function ensureUser(req: OxyAuthRequest, userId: string): OxyRequestUser {
|
|
|
96
99
|
export function getOxyUserId(req: Request): string | null {
|
|
97
100
|
const authReq = req as OxyAuthRequest;
|
|
98
101
|
return (
|
|
99
|
-
normalizeId(authReq.userId) ??
|
|
100
|
-
normalizeId(authReq.user?.id) ??
|
|
101
|
-
normalizeId(authReq.user?._id)
|
|
102
|
+
normalizeId(authReq.userId) ?? normalizeId(authReq.user?.id) ?? normalizeId(authReq.user?._id)
|
|
102
103
|
);
|
|
103
104
|
}
|
|
104
105
|
|
|
@@ -297,7 +298,10 @@ export function createOptionalOxyAuth(
|
|
|
297
298
|
oxy: OxyAuthHost,
|
|
298
299
|
options: OxyAuthMiddlewareOptions = {},
|
|
299
300
|
): RequestHandler {
|
|
300
|
-
const resolveSession = oxy.middleware.auth({
|
|
301
|
+
const resolveSession = oxy.middleware.auth({
|
|
302
|
+
...options.auth,
|
|
303
|
+
optional: true,
|
|
304
|
+
}) as unknown as RequestHandler;
|
|
301
305
|
|
|
302
306
|
return (req, res, next) => {
|
|
303
307
|
if (getOxyUserId(req)) {
|
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
randomUUID,
|
|
3
|
-
sign as signBytes,
|
|
4
|
-
verify as verifyBytes,
|
|
5
|
-
type KeyObject,
|
|
6
|
-
} from 'node:crypto';
|
|
1
|
+
import { randomUUID, sign as signBytes, verify as verifyBytes, type KeyObject } from 'node:crypto';
|
|
7
2
|
import {
|
|
8
3
|
capabilityTicketClaimsSchema,
|
|
9
4
|
type CapabilityTicketClaims,
|
|
@@ -79,7 +74,10 @@ export function issueCapabilityTicket(
|
|
|
79
74
|
): string {
|
|
80
75
|
const ttlSeconds = options.ttlSeconds ?? 60;
|
|
81
76
|
if (!Number.isInteger(ttlSeconds) || ttlSeconds < 1 || ttlSeconds > DEFAULT_MAX_TTL_SECONDS) {
|
|
82
|
-
throw new CapabilityTicketError(
|
|
77
|
+
throw new CapabilityTicketError(
|
|
78
|
+
'ttl_exceeded',
|
|
79
|
+
`Capability ticket TTL must be between 1 and ${DEFAULT_MAX_TTL_SECONDS} seconds`,
|
|
80
|
+
);
|
|
83
81
|
}
|
|
84
82
|
|
|
85
83
|
const issuedAt = Math.floor((options.now ?? new Date()).getTime() / 1_000);
|
|
@@ -91,7 +89,10 @@ export function issueCapabilityTicket(
|
|
|
91
89
|
jti: options.jti ?? randomUUID(),
|
|
92
90
|
});
|
|
93
91
|
if (options.privateKey.asymmetricKeyType !== 'ed25519') {
|
|
94
|
-
throw new CapabilityTicketError(
|
|
92
|
+
throw new CapabilityTicketError(
|
|
93
|
+
'invalid_claims',
|
|
94
|
+
'Capability tickets require an Ed25519 private key',
|
|
95
|
+
);
|
|
95
96
|
}
|
|
96
97
|
const header = { alg: ALGORITHM, typ: TOKEN_TYPE, kid: options.keyId } as const;
|
|
97
98
|
const signingInput = `${base64UrlEncode(JSON.stringify(header))}.${base64UrlEncode(JSON.stringify(payload))}`;
|
|
@@ -117,11 +118,11 @@ export function verifyCapabilityTicket(
|
|
|
117
118
|
}
|
|
118
119
|
const headerRecord = header as Record<string, unknown>;
|
|
119
120
|
if (
|
|
120
|
-
Object.keys(headerRecord).length !== 3
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
121
|
+
Object.keys(headerRecord).length !== 3 ||
|
|
122
|
+
headerRecord.alg !== ALGORITHM ||
|
|
123
|
+
headerRecord.typ !== TOKEN_TYPE ||
|
|
124
|
+
typeof headerRecord.kid !== 'string' ||
|
|
125
|
+
headerRecord.kid.length === 0
|
|
125
126
|
) {
|
|
126
127
|
throw new CapabilityTicketError('malformed', 'Capability ticket header is not supported');
|
|
127
128
|
}
|
|
@@ -135,12 +136,14 @@ export function verifyCapabilityTicket(
|
|
|
135
136
|
} catch {
|
|
136
137
|
throw new CapabilityTicketError('malformed', 'Capability ticket signature is malformed');
|
|
137
138
|
}
|
|
138
|
-
if (
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
139
|
+
if (
|
|
140
|
+
!verifyBytes(
|
|
141
|
+
null,
|
|
142
|
+
Buffer.from(`${encodedHeader}.${encodedPayload}`),
|
|
143
|
+
publicKey,
|
|
144
|
+
providedSignature,
|
|
145
|
+
)
|
|
146
|
+
) {
|
|
144
147
|
throw new CapabilityTicketError('invalid_signature', 'Capability ticket signature is invalid');
|
|
145
148
|
}
|
|
146
149
|
|
|
@@ -158,10 +161,16 @@ export function verifyCapabilityTicket(
|
|
|
158
161
|
throw new CapabilityTicketError('expired', 'Capability ticket has expired');
|
|
159
162
|
}
|
|
160
163
|
if (claims.exp - claims.iat > (options.maximumTtlSeconds ?? DEFAULT_MAX_TTL_SECONDS)) {
|
|
161
|
-
throw new CapabilityTicketError(
|
|
164
|
+
throw new CapabilityTicketError(
|
|
165
|
+
'ttl_exceeded',
|
|
166
|
+
'Capability ticket lifetime exceeds the accepted maximum',
|
|
167
|
+
);
|
|
162
168
|
}
|
|
163
169
|
if (claims.aud !== options.audience) {
|
|
164
|
-
throw new CapabilityTicketError(
|
|
170
|
+
throw new CapabilityTicketError(
|
|
171
|
+
'wrong_audience',
|
|
172
|
+
'Capability ticket audience does not match this app',
|
|
173
|
+
);
|
|
165
174
|
}
|
|
166
175
|
if (options.issuer && claims.iss !== options.issuer) {
|
|
167
176
|
throw new CapabilityTicketError('wrong_issuer', 'Capability ticket issuer is not trusted');
|
package/src/server/index.ts
CHANGED
|
@@ -17,7 +17,11 @@
|
|
|
17
17
|
|
|
18
18
|
// The backend client: OxyServices plus the service-token lane, middleware,
|
|
19
19
|
// account events and the service-only namespace methods.
|
|
20
|
-
export {
|
|
20
|
+
export {
|
|
21
|
+
OxyServer,
|
|
22
|
+
ServiceCredentialMismatchError,
|
|
23
|
+
ANONYMOUS_SERVICE_TOKEN_RETRY_MS,
|
|
24
|
+
} from './OxyServer';
|
|
21
25
|
export type { OxyServerConfig, ServiceTokenResponse } from './OxyServer';
|
|
22
26
|
export {
|
|
23
27
|
ServerAppsApi,
|
|
@@ -200,7 +204,11 @@ export type {
|
|
|
200
204
|
// The identity-key enumeration itself is platform-neutral (`src/utils/`) so the
|
|
201
205
|
// client namespaces and this Node-only subscriber sweep the SAME list — a second
|
|
202
206
|
// copy is what let `updateAccount` and `updateProfile` drift apart.
|
|
203
|
-
export {
|
|
207
|
+
export {
|
|
208
|
+
evictOxyIdentityCache,
|
|
209
|
+
oxyUserByIdCacheKey,
|
|
210
|
+
OXY_IDENTITY_CACHE_PREFIXES,
|
|
211
|
+
} from '../utils/identityCacheSweep';
|
|
204
212
|
export type { OxyIdentityCacheEvictor } from '../utils/identityCacheSweep';
|
|
205
213
|
|
|
206
214
|
// Registrable-apex (eTLD+1) derivation via the Public Suffix List — the SINGLE
|
|
@@ -208,7 +216,11 @@ export type { OxyIdentityCacheEvictor } from '../utils/identityCacheSweep';
|
|
|
208
216
|
// Pure host handling (no browser deps), so it is safe on the server subpath and
|
|
209
217
|
// lets `@oxy.so/api` derive `auth.<apex>` without duplicating PSL logic.
|
|
210
218
|
export { registrableApex } from '../utils/registrableApex';
|
|
211
|
-
export {
|
|
219
|
+
export {
|
|
220
|
+
isLoopbackOrigin,
|
|
221
|
+
isOfficialWebOrigin,
|
|
222
|
+
isAllowedDeviceJoinOrigin,
|
|
223
|
+
} from '../utils/officialOrigins';
|
|
212
224
|
|
|
213
225
|
export { createEcosystemTraffic } from './traffic';
|
|
214
226
|
export type { EcosystemTrafficOptions } from './traffic';
|
|
@@ -221,9 +233,20 @@ export { observeNodeHttp, withoutNodeHttpObservation } from './trafficNodeHttp';
|
|
|
221
233
|
* exported from this subpath rather than the root barrel because it reaches the
|
|
222
234
|
* container credentials endpoint.
|
|
223
235
|
*/
|
|
224
|
-
export {
|
|
225
|
-
|
|
236
|
+
export {
|
|
237
|
+
attestWorkloadIdentity,
|
|
238
|
+
canAttestWorkloadIdentity,
|
|
239
|
+
requestWorkloadServiceToken,
|
|
240
|
+
} from './workloadIdentity';
|
|
241
|
+
export type {
|
|
242
|
+
WorkloadAttestation,
|
|
243
|
+
WorkloadServiceToken,
|
|
244
|
+
WorkloadServiceTokenOptions,
|
|
245
|
+
} from './workloadIdentity';
|
|
226
246
|
|
|
227
|
-
export {
|
|
247
|
+
export {
|
|
248
|
+
OXY_SERVICE_TOKEN_MAX_LIFETIME_SECONDS,
|
|
249
|
+
hasBoundedServiceTokenLifetime,
|
|
250
|
+
} from './serviceTokenLifetime';
|
|
228
251
|
export { signInAgentAccount } from './agentAccount';
|
|
229
252
|
export type { AgentAccountSigner } from './agentAccount';
|
|
@@ -1,41 +1,63 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
canonicalCapabilityJson,
|
|
3
|
+
capabilityTicketClaimsSchema,
|
|
4
|
+
type CapabilityTicketClaims,
|
|
5
|
+
} from '@oxy.so/contracts';
|
|
6
|
+
import {
|
|
7
|
+
verifyCapabilityTicket,
|
|
8
|
+
type CapabilityTicketVerificationOptions,
|
|
9
|
+
} from './capabilityTicket';
|
|
3
10
|
import type { CapabilityTicketIntrospection } from './namespaces';
|
|
4
11
|
|
|
5
12
|
export interface LiveCapabilityTicketVerifierOptions extends CapabilityTicketVerificationOptions {
|
|
6
13
|
issuer: string;
|
|
7
|
-
introspect: (
|
|
14
|
+
introspect: (
|
|
15
|
+
ticket: string,
|
|
16
|
+
options: { signal: AbortSignal },
|
|
17
|
+
) => Promise<CapabilityTicketIntrospection>;
|
|
8
18
|
/** Network bound only; does not establish an authority freshness policy. */
|
|
9
19
|
timeoutMs?: number;
|
|
10
20
|
}
|
|
11
21
|
|
|
12
22
|
/** Local signature + live receiving-service introspection; no cache or retry. */
|
|
13
23
|
export function createLiveCapabilityTicketVerifier(options: LiveCapabilityTicketVerifierOptions) {
|
|
14
|
-
if (typeof options.issuer !== 'string' || options.issuer.trim() === '')
|
|
24
|
+
if (typeof options.issuer !== 'string' || options.issuer.trim() === '')
|
|
25
|
+
throw new Error('A trusted Capability issuer is required');
|
|
15
26
|
const timeoutMs = options.timeoutMs ?? 5000;
|
|
16
27
|
if (!Number.isInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 60000) {
|
|
17
28
|
throw new Error('Capability introspection timeout must be between 1 and 60000ms');
|
|
18
29
|
}
|
|
19
|
-
return async (
|
|
30
|
+
return async (
|
|
31
|
+
ticket: string,
|
|
32
|
+
input: { signal?: AbortSignal } = {},
|
|
33
|
+
): Promise<CapabilityTicketClaims> => {
|
|
20
34
|
input.signal?.throwIfAborted();
|
|
21
35
|
const signed = verifyCapabilityTicket(ticket, options);
|
|
22
36
|
if (!signed.catalog) throw new Error('Internal MCP requires a signed catalogue binding');
|
|
23
37
|
const controller = new AbortController();
|
|
24
38
|
const abort = () => controller.abort(input.signal?.reason);
|
|
25
39
|
input.signal?.addEventListener('abort', abort, { once: true });
|
|
26
|
-
const timer = setTimeout(
|
|
40
|
+
const timer = setTimeout(
|
|
41
|
+
() => controller.abort(new Error('Capability introspection timed out')),
|
|
42
|
+
timeoutMs,
|
|
43
|
+
);
|
|
27
44
|
let rejectAbort: (() => void) | undefined;
|
|
28
45
|
try {
|
|
29
46
|
const interrupted = new Promise<never>((_resolve, reject) => {
|
|
30
|
-
rejectAbort = () =>
|
|
47
|
+
rejectAbort = () =>
|
|
48
|
+
reject(controller.signal.reason ?? new Error('Capability introspection aborted'));
|
|
31
49
|
controller.signal.addEventListener('abort', rejectAbort, { once: true });
|
|
32
50
|
if (controller.signal.aborted) rejectAbort();
|
|
33
51
|
});
|
|
34
|
-
const result = await Promise.race([
|
|
52
|
+
const result = await Promise.race([
|
|
53
|
+
options.introspect(ticket, { signal: controller.signal }),
|
|
54
|
+
interrupted,
|
|
55
|
+
]);
|
|
35
56
|
controller.signal.throwIfAborted();
|
|
36
57
|
if (result.active !== true) throw new Error('Capability ticket is inactive');
|
|
37
58
|
const live = capabilityTicketClaimsSchema.parse(result.claims);
|
|
38
|
-
if (canonicalCapabilityJson(live) !== canonicalCapabilityJson(signed))
|
|
59
|
+
if (canonicalCapabilityJson(live) !== canonicalCapabilityJson(signed))
|
|
60
|
+
throw new Error('Capability introspection claims mismatch');
|
|
39
61
|
// An authority response delayed past expiry cannot extend the signed grant.
|
|
40
62
|
return verifyCapabilityTicket(ticket, options);
|
|
41
63
|
} finally {
|