@oxy.so/core 7.22.0 → 7.23.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/server/OxyServer.js +29 -0
- package/dist/esm/server/OxyServer.js +29 -0
- package/dist/types/server/OxyServer.d.ts +16 -0
- package/package.json +1 -1
- package/src/server/OxyServer.ts +29 -0
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@ The package exposes two public entry points:
|
|
|
31
31
|
- `@oxy.so/core/session` — `SessionClient`, the account dialog controller, auth-state stores, cold boot
|
|
32
32
|
- `@oxy.so/core/crypto` — `KeyManager`, `SignatureService`, `RecoveryPhraseService` and the primitives under them
|
|
33
33
|
- `@oxy.so/core/civic`, `@oxy.so/core/inference`, `@oxy.so/core/logger`
|
|
34
|
-
- `@oxy.so/core/server` — `OxyServer` (workload service tokens, `middleware.*`, `withUser` / `asUser`, `appPermissionsFor`, `accountAccessFor`, `applicationAccessFor`) and the Express helpers (`createOxyRateLimit`, `createOxyAuthMiddleware`, `getOxyUserId`, `getRequiredOxyUserId`, `createOxyCors`, `createOxySocketCors`, `createOxySecurityHeaders`, `buildOxyCspDirectives`, `safeFetch`, `verifySecret`, and request types)
|
|
34
|
+
- `@oxy.so/core/server` — `OxyServer` (workload service tokens, `middleware.*`, `withUser` / `asUser`, `isFirstPartyServiceUrl`, `appPermissionsFor`, `accountAccessFor`, `applicationAccessFor`) and the Express helpers (`createOxyRateLimit`, `createOxyAuthMiddleware`, `getOxyUserId`, `getRequiredOxyUserId`, `createOxyCors`, `createOxySocketCors`, `createOxySecurityHeaders`, `buildOxyCspDirectives`, `safeFetch`, `verifySecret`, and request types)
|
|
35
35
|
|
|
36
36
|
Heavy pieces have their own entries so an app ships only what it uses; React Native and browser bundles never import Express. The full API map is [docs/api.mdx](./docs/api.mdx).
|
|
37
37
|
|
|
@@ -49,6 +49,7 @@ const OxyServices_1 = require("../OxyServices");
|
|
|
49
49
|
const HttpService_1 = require("../HttpService");
|
|
50
50
|
const authenticatedFetch_1 = require("../authenticatedFetch");
|
|
51
51
|
const requestSignal_1 = require("../utils/requestSignal");
|
|
52
|
+
const officialOrigins_1 = require("../utils/officialOrigins");
|
|
52
53
|
const errorUtils_1 = require("../utils/errorUtils");
|
|
53
54
|
const logger_1 = require("../logger");
|
|
54
55
|
const workloadIdentity_1 = require("./workloadIdentity");
|
|
@@ -193,6 +194,34 @@ class OxyServer extends OxyServices_1.OxyServices {
|
|
|
193
194
|
applicationAccessFor: (req, applicationId, options) => this.applicationAccessFor(req, applicationId, options),
|
|
194
195
|
});
|
|
195
196
|
}
|
|
197
|
+
/**
|
|
198
|
+
* Whether `url` names Oxy's own API — the first-party service this server is
|
|
199
|
+
* configured for (`baseURL`), when that origin is an official Oxy origin.
|
|
200
|
+
*
|
|
201
|
+
* For a URL a caller chose (a customer webhook endpoint, say) that may name
|
|
202
|
+
* Oxy itself: `true` sends it through this server
|
|
203
|
+
* (`createServiceClient({ baseURL: oxy.baseURL, audience: 'oxy-api' }).fetch`),
|
|
204
|
+
* `false` through `safeFetch`. Inside the VPC `api.oxy.so` resolves to a
|
|
205
|
+
* private address, which `safeFetch` refuses by design.
|
|
206
|
+
*
|
|
207
|
+
* The comparison is by exact origin (scheme, host and port): a look-alike
|
|
208
|
+
* (`api.oxy.so.evil.com`), another official host, a credential-carrying URL
|
|
209
|
+
* or anything unparseable is `false`, and so goes on to `safeFetch`'s
|
|
210
|
+
* checks. Never decide first-party by hostname suffix or branding.
|
|
211
|
+
*/
|
|
212
|
+
isFirstPartyServiceUrl(url) {
|
|
213
|
+
let target;
|
|
214
|
+
try {
|
|
215
|
+
target = new URL(String(url));
|
|
216
|
+
}
|
|
217
|
+
catch {
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
if (!['http:', 'https:'].includes(target.protocol) || target.username || target.password)
|
|
221
|
+
return false;
|
|
222
|
+
const own = new URL(this.baseURL).origin;
|
|
223
|
+
return target.origin === own && (0, officialOrigins_1.isOfficialWebOrigin)(own);
|
|
224
|
+
}
|
|
196
225
|
/** A product SDK transport authenticated only by this process's workload. */
|
|
197
226
|
createServiceClient(options) {
|
|
198
227
|
const origin = new URL(options.baseURL);
|
|
@@ -46,6 +46,7 @@ import { OxyServices } from '../OxyServices.js';
|
|
|
46
46
|
import { HttpService } from '../HttpService.js';
|
|
47
47
|
import { createAuthenticatedFetch } from '../authenticatedFetch.js';
|
|
48
48
|
import { awaitRequestSignal } from '../utils/requestSignal.js';
|
|
49
|
+
import { isOfficialWebOrigin } from '../utils/officialOrigins.js';
|
|
49
50
|
import { createCancelledError, extractErrorStatus } from '../utils/errorUtils.js';
|
|
50
51
|
import { logger } from '../logger/index.js';
|
|
51
52
|
import { canAttestWorkloadIdentity, requestWorkloadServiceToken, } from './workloadIdentity.js';
|
|
@@ -189,6 +190,34 @@ export class OxyServer extends OxyServices {
|
|
|
189
190
|
applicationAccessFor: (req, applicationId, options) => this.applicationAccessFor(req, applicationId, options),
|
|
190
191
|
});
|
|
191
192
|
}
|
|
193
|
+
/**
|
|
194
|
+
* Whether `url` names Oxy's own API — the first-party service this server is
|
|
195
|
+
* configured for (`baseURL`), when that origin is an official Oxy origin.
|
|
196
|
+
*
|
|
197
|
+
* For a URL a caller chose (a customer webhook endpoint, say) that may name
|
|
198
|
+
* Oxy itself: `true` sends it through this server
|
|
199
|
+
* (`createServiceClient({ baseURL: oxy.baseURL, audience: 'oxy-api' }).fetch`),
|
|
200
|
+
* `false` through `safeFetch`. Inside the VPC `api.oxy.so` resolves to a
|
|
201
|
+
* private address, which `safeFetch` refuses by design.
|
|
202
|
+
*
|
|
203
|
+
* The comparison is by exact origin (scheme, host and port): a look-alike
|
|
204
|
+
* (`api.oxy.so.evil.com`), another official host, a credential-carrying URL
|
|
205
|
+
* or anything unparseable is `false`, and so goes on to `safeFetch`'s
|
|
206
|
+
* checks. Never decide first-party by hostname suffix or branding.
|
|
207
|
+
*/
|
|
208
|
+
isFirstPartyServiceUrl(url) {
|
|
209
|
+
let target;
|
|
210
|
+
try {
|
|
211
|
+
target = new URL(String(url));
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
if (!['http:', 'https:'].includes(target.protocol) || target.username || target.password)
|
|
217
|
+
return false;
|
|
218
|
+
const own = new URL(this.baseURL).origin;
|
|
219
|
+
return target.origin === own && isOfficialWebOrigin(own);
|
|
220
|
+
}
|
|
192
221
|
/** A product SDK transport authenticated only by this process's workload. */
|
|
193
222
|
createServiceClient(options) {
|
|
194
223
|
const origin = new URL(options.baseURL);
|
|
@@ -196,6 +196,22 @@ export declare class OxyServer extends OxyServices {
|
|
|
196
196
|
*/
|
|
197
197
|
readonly middleware: OxyMiddleware;
|
|
198
198
|
constructor(config: OxyServerConfig);
|
|
199
|
+
/**
|
|
200
|
+
* Whether `url` names Oxy's own API — the first-party service this server is
|
|
201
|
+
* configured for (`baseURL`), when that origin is an official Oxy origin.
|
|
202
|
+
*
|
|
203
|
+
* For a URL a caller chose (a customer webhook endpoint, say) that may name
|
|
204
|
+
* Oxy itself: `true` sends it through this server
|
|
205
|
+
* (`createServiceClient({ baseURL: oxy.baseURL, audience: 'oxy-api' }).fetch`),
|
|
206
|
+
* `false` through `safeFetch`. Inside the VPC `api.oxy.so` resolves to a
|
|
207
|
+
* private address, which `safeFetch` refuses by design.
|
|
208
|
+
*
|
|
209
|
+
* The comparison is by exact origin (scheme, host and port): a look-alike
|
|
210
|
+
* (`api.oxy.so.evil.com`), another official host, a credential-carrying URL
|
|
211
|
+
* or anything unparseable is `false`, and so goes on to `safeFetch`'s
|
|
212
|
+
* checks. Never decide first-party by hostname suffix or branding.
|
|
213
|
+
*/
|
|
214
|
+
isFirstPartyServiceUrl(url: string | URL): boolean;
|
|
199
215
|
/** A product SDK transport authenticated only by this process's workload. */
|
|
200
216
|
createServiceClient(options: ServiceClientOptions): ServiceHttpClient;
|
|
201
217
|
/** Live service authority; human sessions and keys have a separate resource contract. */
|
package/package.json
CHANGED
package/src/server/OxyServer.ts
CHANGED
|
@@ -66,6 +66,7 @@ import { OxyServices, type OxyConfig } from '../OxyServices';
|
|
|
66
66
|
import { HttpService } from '../HttpService';
|
|
67
67
|
import { createAuthenticatedFetch } from '../authenticatedFetch';
|
|
68
68
|
import { awaitRequestSignal } from '../utils/requestSignal';
|
|
69
|
+
import { isOfficialWebOrigin } from '../utils/officialOrigins';
|
|
69
70
|
import { createCancelledError, extractErrorStatus } from '../utils/errorUtils';
|
|
70
71
|
import type { HttpMethod, ServiceLane } from '../client/context';
|
|
71
72
|
import type { RequestOptions, ResponsePayload } from '../HttpService';
|
|
@@ -410,6 +411,34 @@ export class OxyServer extends OxyServices {
|
|
|
410
411
|
});
|
|
411
412
|
}
|
|
412
413
|
|
|
414
|
+
/**
|
|
415
|
+
* Whether `url` names Oxy's own API — the first-party service this server is
|
|
416
|
+
* configured for (`baseURL`), when that origin is an official Oxy origin.
|
|
417
|
+
*
|
|
418
|
+
* For a URL a caller chose (a customer webhook endpoint, say) that may name
|
|
419
|
+
* Oxy itself: `true` sends it through this server
|
|
420
|
+
* (`createServiceClient({ baseURL: oxy.baseURL, audience: 'oxy-api' }).fetch`),
|
|
421
|
+
* `false` through `safeFetch`. Inside the VPC `api.oxy.so` resolves to a
|
|
422
|
+
* private address, which `safeFetch` refuses by design.
|
|
423
|
+
*
|
|
424
|
+
* The comparison is by exact origin (scheme, host and port): a look-alike
|
|
425
|
+
* (`api.oxy.so.evil.com`), another official host, a credential-carrying URL
|
|
426
|
+
* or anything unparseable is `false`, and so goes on to `safeFetch`'s
|
|
427
|
+
* checks. Never decide first-party by hostname suffix or branding.
|
|
428
|
+
*/
|
|
429
|
+
isFirstPartyServiceUrl(url: string | URL): boolean {
|
|
430
|
+
let target: URL;
|
|
431
|
+
try {
|
|
432
|
+
target = new URL(String(url));
|
|
433
|
+
} catch {
|
|
434
|
+
return false;
|
|
435
|
+
}
|
|
436
|
+
if (!['http:', 'https:'].includes(target.protocol) || target.username || target.password)
|
|
437
|
+
return false;
|
|
438
|
+
const own = new URL(this.baseURL).origin;
|
|
439
|
+
return target.origin === own && isOfficialWebOrigin(own);
|
|
440
|
+
}
|
|
441
|
+
|
|
413
442
|
/** A product SDK transport authenticated only by this process's workload. */
|
|
414
443
|
createServiceClient(options: ServiceClientOptions): ServiceHttpClient {
|
|
415
444
|
const origin = new URL(options.baseURL);
|