@oxy.so/core 1.7.4 → 1.8.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/dist/cjs/.tsbuildinfo +1 -1
- package/dist/cjs/inference/OxyInferenceClient.js +8 -3
- package/dist/cjs/mixins/OxyServices.utility.js +23 -6
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/inference/OxyInferenceClient.js +8 -3
- package/dist/esm/mixins/OxyServices.utility.js +23 -6
- package/dist/types/.tsbuildinfo +1 -1
- package/dist/types/inference/OxyInferenceClient.d.ts +15 -4
- package/dist/types/mixins/OxyServices.utility.d.ts +8 -0
- package/dist/types/server/auth.d.ts +2 -0
- package/package.json +2 -2
- package/src/inference/OxyInferenceClient.ts +15 -3
- package/src/inference/__tests__/OxyInferenceClient.test.ts +17 -0
- package/src/mixins/OxyServices.utility.ts +33 -6
- package/src/mixins/__tests__/serviceAuth.test.ts +57 -0
- package/src/server/auth.ts +2 -0
|
@@ -140,9 +140,14 @@ export class OxyInferenceClient {
|
|
|
140
140
|
* both see the PUBLIC catalogue; only an internal/system application's
|
|
141
141
|
* service token sees internal-only routes.
|
|
142
142
|
*
|
|
143
|
-
* **`[]` is a normal answer
|
|
144
|
-
*
|
|
145
|
-
*
|
|
143
|
+
* **`[]` is a normal answer**: a route is not publicly exposed until
|
|
144
|
+
* somebody has reviewed the right to resell it. An official application's
|
|
145
|
+
* credential also sees the internal catalogue Oxy keeps in step with
|
|
146
|
+
* Kaana automatically.
|
|
147
|
+
*
|
|
148
|
+
* Each entry's `capabilities.reasoningEfforts` lists the efforts a request
|
|
149
|
+
* may name in `reasoning.effort` (empty: none), and `releasedAt`, when
|
|
150
|
+
* present, is the date the upstream provider published the model.
|
|
146
151
|
*/
|
|
147
152
|
async listModels(options = {}) {
|
|
148
153
|
const body = await __classPrivateFieldGet(this, _OxyInferenceClient_instances, "m", _OxyInferenceClient_request).call(this, 'GET', '/v1/models', { ...(options.signal === undefined ? {} : { signal: options.signal }) });
|
|
@@ -493,6 +493,7 @@ export function OxyServicesUtilityMixin(Base) {
|
|
|
493
493
|
return onError(error);
|
|
494
494
|
return res.status(401).json(error);
|
|
495
495
|
}
|
|
496
|
+
const tier = decoded.tier === 'internal' ? 'internal' : 'external';
|
|
496
497
|
// Read delegated user ID from header
|
|
497
498
|
const oxyUserIdRaw = req.headers['x-oxy-user-id'];
|
|
498
499
|
const oxyUserId = isExactNonEmptyServiceClaim(oxyUserIdRaw) ? oxyUserIdRaw : null;
|
|
@@ -513,12 +514,21 @@ export function OxyServicesUtilityMixin(Base) {
|
|
|
513
514
|
return onError(error);
|
|
514
515
|
return res.status(401).json(error);
|
|
515
516
|
}
|
|
516
|
-
//
|
|
517
|
-
//
|
|
518
|
-
//
|
|
519
|
-
//
|
|
520
|
-
|
|
521
|
-
|
|
517
|
+
// One of Oxy's own applications acts for a user without a grant:
|
|
518
|
+
// inside the ecosystem that is trust, not consent. It still acts
|
|
519
|
+
// with the USER's authority only — what that user may do is for the
|
|
520
|
+
// receiving service to check.
|
|
521
|
+
if (oxyUserId && tier === 'internal') {
|
|
522
|
+
req.userId = oxyUserId;
|
|
523
|
+
req.user = { id: oxyUserId };
|
|
524
|
+
req.serviceActingAs = { userId: oxyUserId, scopes: [] };
|
|
525
|
+
}
|
|
526
|
+
else if (oxyUserId) {
|
|
527
|
+
// C3: an EXTERNAL service may only act as a user when an explicit
|
|
528
|
+
// ServiceActingAs grant exists for that (appId, userId) pair.
|
|
529
|
+
// Without the grant we MUST refuse — silently attaching
|
|
530
|
+
// `req.userId = oxyUserId` would let any service impersonate
|
|
531
|
+
// any user simply by setting the header.
|
|
522
532
|
const grant = await oxyInstance.verifyServiceActingAs(appId, oxyUserId);
|
|
523
533
|
if (!grant || !grant.authorized) {
|
|
524
534
|
logger.warn('[oxy.auth] Service token rejected — no delegation grant', {
|
|
@@ -569,6 +579,7 @@ export function OxyServicesUtilityMixin(Base) {
|
|
|
569
579
|
ownerAccountId,
|
|
570
580
|
scopes: Array.isArray(decoded.scopes) ? decoded.scopes : [],
|
|
571
581
|
environment,
|
|
582
|
+
tier,
|
|
572
583
|
};
|
|
573
584
|
if (debug) {
|
|
574
585
|
logger.debug(`[oxy.auth] Service token OK app=${decoded.appName} delegateUser=${oxyUserId || '(none)'}`, {
|
|
@@ -1001,6 +1012,12 @@ export function OxyServicesUtilityMixin(Base) {
|
|
|
1001
1012
|
});
|
|
1002
1013
|
return;
|
|
1003
1014
|
}
|
|
1015
|
+
// Oxy's own applications are trusted app to app; scopes are the
|
|
1016
|
+
// external lane.
|
|
1017
|
+
if (req.serviceApp.tier === 'internal') {
|
|
1018
|
+
next();
|
|
1019
|
+
return;
|
|
1020
|
+
}
|
|
1004
1021
|
const appHasScope = appScopes.includes(scope);
|
|
1005
1022
|
const delegationHasScope = delegatedScopes.includes(scope);
|
|
1006
1023
|
const hasRequiredScope = req.serviceActingAs
|