@webpieces/core-context 0.4.966 → 0.4.968
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/package.json +2 -2
- package/src/VendorAuthMinter.d.ts +25 -5
- package/src/VendorAuthMinter.js +15 -5
- package/src/VendorAuthMinter.js.map +1 -1
- package/src/VendorAuthRolesCompileAssertions.d.ts +5 -0
- package/src/VendorAuthRolesCompileAssertions.js +40 -0
- package/src/VendorAuthRolesCompileAssertions.js.map +1 -1
- package/src/VendorAuthVerifier.d.ts +23 -4
- package/src/VendorAuthVerifier.js +13 -4
- package/src/VendorAuthVerifier.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webpieces/core-context",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.968",
|
|
4
4
|
"description": "AsyncLocalStorage-based context management for request-scoped data",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"access": "public"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@webpieces/core-util": "0.4.
|
|
25
|
+
"@webpieces/core-util": "0.4.968",
|
|
26
26
|
"@inversifyjs/binding-decorators": "1.1.5",
|
|
27
27
|
"inversify": "7.10.4",
|
|
28
28
|
"reflect-metadata": "0.2.2"
|
|
@@ -79,15 +79,25 @@ export declare class SignableRequest {
|
|
|
79
79
|
* itself inject a typed RPC client. Validating a credential server-side is the verifier's job, on
|
|
80
80
|
* the server.
|
|
81
81
|
*
|
|
82
|
-
* ## It FAILS
|
|
82
|
+
* ## It FAILS AT CLIENT CREATION
|
|
83
83
|
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
84
|
+
* `createRpcClient` refuses a contract with an endpoint whose only declared credential this client
|
|
85
|
+
* could send is `vendorAuth(name)` when no minter is bound, or the bound minter's {@link supportedVendorNames}
|
|
86
|
+
* does not list `name` — it throws `MissingClientCredentialError` naming the contract, the method and
|
|
87
|
+
* the binding, before any call is made (#1362). An endpoint that also declares a credential this
|
|
88
|
+
* client CAN send (OIDC, a held shared secret) needs no minter. There is no framework default and
|
|
89
|
+
* there never will be one.
|
|
87
90
|
*
|
|
88
91
|
* ```typescript
|
|
89
92
|
* // the CLI / client process's BindModule
|
|
90
93
|
* binder.bind(VENDOR_AUTH_MINTER).to(MyVendorAuthMinter).inSingletonScope();
|
|
94
|
+
*
|
|
95
|
+
* class MyVendorAuthMinter extends VendorAuthMinter {
|
|
96
|
+
* override supportedVendorNames(): readonly string[] {
|
|
97
|
+
* return ['clover']; // every name mint()/refresh() below handle
|
|
98
|
+
* }
|
|
99
|
+
* // mint(...) and refresh(...)
|
|
100
|
+
* }
|
|
91
101
|
* ```
|
|
92
102
|
*
|
|
93
103
|
* ## Refresh-once, and who owns serializing it
|
|
@@ -100,6 +110,16 @@ export declare class SignableRequest {
|
|
|
100
110
|
* credential source; it never authenticates through the call it is refreshing for.
|
|
101
111
|
*/
|
|
102
112
|
export declare abstract class VendorAuthMinter {
|
|
113
|
+
/**
|
|
114
|
+
* Every vendor name {@link mint} and {@link refresh} handle — each `vendorAuth(name)` this process
|
|
115
|
+
* can call. A METHOD beside `mint` so the list sits next to the code that dispatches on those
|
|
116
|
+
* names: add a vendor to both in one edit. REQUIRED, no default.
|
|
117
|
+
*
|
|
118
|
+
* Called at CLIENT CREATION by `createRpcClient` (#1362), to refuse a contract it cannot call. It
|
|
119
|
+
* MUST therefore be PURE — return a fixed list, with no I/O, no network, no credential work and no
|
|
120
|
+
* side effect. {@link mint} and {@link refresh} are never called then.
|
|
121
|
+
*/
|
|
122
|
+
abstract supportedVendorNames(): readonly string[];
|
|
103
123
|
/**
|
|
104
124
|
* Produce the headers that authenticate ONE outbound request, or throw to refuse to send it. An
|
|
105
125
|
* empty map is legal and means "this vendor needs no header".
|
|
@@ -119,6 +139,6 @@ export declare abstract class VendorAuthMinter {
|
|
|
119
139
|
* DI identifier for the optional {@link VendorAuthMinter} binding — the OUTBOUND role only. It is a
|
|
120
140
|
* Symbol (not the class) so the app container's inversify autobind never auto-constructs this token,
|
|
121
141
|
* keeping `@optional() @inject(VENDOR_AUTH_MINTER)` correct — undefined when unbound, which is what
|
|
122
|
-
*
|
|
142
|
+
* lets `createRpcClient` refuse a contract this process cannot call.
|
|
123
143
|
*/
|
|
124
144
|
export declare const VENDOR_AUTH_MINTER: unique symbol;
|
package/src/VendorAuthMinter.js
CHANGED
|
@@ -79,15 +79,25 @@ exports.SignableRequest = SignableRequest;
|
|
|
79
79
|
* itself inject a typed RPC client. Validating a credential server-side is the verifier's job, on
|
|
80
80
|
* the server.
|
|
81
81
|
*
|
|
82
|
-
* ## It FAILS
|
|
82
|
+
* ## It FAILS AT CLIENT CREATION
|
|
83
83
|
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
84
|
+
* `createRpcClient` refuses a contract with an endpoint whose only declared credential this client
|
|
85
|
+
* could send is `vendorAuth(name)` when no minter is bound, or the bound minter's {@link supportedVendorNames}
|
|
86
|
+
* does not list `name` — it throws `MissingClientCredentialError` naming the contract, the method and
|
|
87
|
+
* the binding, before any call is made (#1362). An endpoint that also declares a credential this
|
|
88
|
+
* client CAN send (OIDC, a held shared secret) needs no minter. There is no framework default and
|
|
89
|
+
* there never will be one.
|
|
87
90
|
*
|
|
88
91
|
* ```typescript
|
|
89
92
|
* // the CLI / client process's BindModule
|
|
90
93
|
* binder.bind(VENDOR_AUTH_MINTER).to(MyVendorAuthMinter).inSingletonScope();
|
|
94
|
+
*
|
|
95
|
+
* class MyVendorAuthMinter extends VendorAuthMinter {
|
|
96
|
+
* override supportedVendorNames(): readonly string[] {
|
|
97
|
+
* return ['clover']; // every name mint()/refresh() below handle
|
|
98
|
+
* }
|
|
99
|
+
* // mint(...) and refresh(...)
|
|
100
|
+
* }
|
|
91
101
|
* ```
|
|
92
102
|
*
|
|
93
103
|
* ## Refresh-once, and who owns serializing it
|
|
@@ -106,7 +116,7 @@ exports.VendorAuthMinter = VendorAuthMinter;
|
|
|
106
116
|
* DI identifier for the optional {@link VendorAuthMinter} binding — the OUTBOUND role only. It is a
|
|
107
117
|
* Symbol (not the class) so the app container's inversify autobind never auto-constructs this token,
|
|
108
118
|
* keeping `@optional() @inject(VENDOR_AUTH_MINTER)` correct — undefined when unbound, which is what
|
|
109
|
-
*
|
|
119
|
+
* lets `createRpcClient` refuse a contract this process cannot call.
|
|
110
120
|
*/
|
|
111
121
|
// webpieces-disable no-symbol-di-tokens -- optional DI token: must be a Symbol so the app container's autobind never auto-constructs this token, keeping @optional() @inject(...) correct (undefined when unbound)
|
|
112
122
|
exports.VENDOR_AUTH_MINTER = Symbol.for('VendorAuthMinter');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VendorAuthMinter.js","sourceRoot":"","sources":["../../../../../packages/core/core-context/src/VendorAuthMinter.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"VendorAuthMinter.js","sourceRoot":"","sources":["../../../../../packages/core/core-context/src/VendorAuthMinter.ts"],"names":[],"mappings":";;;AACA;;;;;;;;;;;;;;GAcG;AACH,MAAa,eAAe;IAGJ;IAEA;IAEA;IAOA;IAEA;IAEA;IAjBpB;IACI,kEAAkE;IAClD,GAAW;IAC3B,oCAAoC;IACpB,UAAkB;IAClC,uEAAuE;IACvD,IAAwB;IACxC;;;;;OAKG;IACa,OAAoC;IACpD,+DAA+D;IAC/C,YAAoB;IACpC,wDAAwD;IACxC,UAAkB;QAflB,QAAG,GAAH,GAAG,CAAQ;QAEX,eAAU,GAAV,UAAU,CAAQ;QAElB,SAAI,GAAJ,IAAI,CAAoB;QAOxB,YAAO,GAAP,OAAO,CAA6B;QAEpC,iBAAY,GAAZ,YAAY,CAAQ;QAEpB,eAAU,GAAV,UAAU,CAAQ;IACnC,CAAC;CACP;AApBD,0CAoBC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DG;AACH,MAAsB,gBAAgB;CA2BrC;AA3BD,4CA2BC;AAED;;;;;GAKG;AACH,mNAAmN;AACtM,QAAA,kBAAkB,GAAG,MAAM,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC","sourcesContent":["\n/**\n * ONE outbound request, as the thing being authenticated — what {@link VendorAuthMinter.mint}\n * receives. Data only.\n *\n * Every field a real vendor scheme needs is here, and the two that make it correct are {@link url}\n * and {@link body}:\n *\n * - {@link url} is the FINAL absolute URL, after every app filter has had its say. A signature\n * computed over the pre-filter URL authenticates a request nobody sent — and for a partner-webhook\n * client the pre-filter URL is the client's own service name, which is not a destination at all.\n * - {@link body} is the EXACT serialized bytes the transport will send, not the DTO. Serialization\n * happens before the filter chain runs and the transport sends this same string verbatim, so the\n * bytes signed and the bytes sent cannot differ. A library that re-serializes internally signs one\n * byte sequence and sends another, and the failure is silent.\n */\nexport class SignableRequest {\n constructor(\n /** The FINAL absolute URL this request is about to be sent to. */\n public readonly url: string,\n /** The HTTP method, e.g. 'POST'. */\n public readonly httpMethod: string,\n /** The EXACT serialized body, or undefined for a call with no body. */\n public readonly body: string | undefined,\n /**\n * The headers as they stand. READ-ONLY here: a scheme that signs existing headers (a\n * timestamp another filter set, a content-type) reads them, and the headers the minter\n * returns are merged by the caller — so there is exactly one place headers are added and it\n * is the return value.\n */\n public readonly headers: ReadonlyMap<string, string>,\n /** The API contract's class name, e.g. 'PartnerWebhookApi'. */\n public readonly contractName: string,\n /** The contract method being called, e.g. 'deliver'. */\n public readonly methodName: string,\n ) {}\n}\n\n/**\n * VendorAuthMinter - the OUTBOUND half of `vendorAuth(name)`: the app-bound mechanism that presents a\n * credential in a scheme the FRAMEWORK does not know when THIS process calls a vendor / partner.\n * `name` selects the vendor, so one minter serves every vendor this process calls.\n *\n * It is one of TWO independent, optional DI roles behind the one contract declaration\n * `vendorAuth(name)` — the direction is decided by whether this runtime is SENDING or RECEIVING:\n *\n * | | who presents the credential | who checks it | the role this process binds |\n * |---|---|---|---|\n * | WE call a vendor / partner | us | them | `VendorAuthMinter` ({@link mint}, {@link refresh}) |\n * | a vendor calls US | them | us | `VendorAuthVerifier` (`verify`) |\n *\n * ## Why two roles and not one hook (#1358)\n *\n * The outbound RPC client (`NodeProxyClient`) is constructed for EVERY typed client, including the\n * ordinary `oidcSameProject()` client an inbound verifier uses to look a credential up. When one\n * binding served both directions, every outbound proxy constructed the app's inbound verifier — and\n * a verifier that depends on an RPC proxy closed a construction cycle (`Circular dependency found`)\n * before the process could listen. The outbound client therefore depends on THIS role only, and the\n * server's `AuthFilter` on the verifier only, so the graph is acyclic whatever order the bindings\n * were registered in. A client-only process implements no meaningless `verify`, and a server-only\n * one no meaningless `mint` / `refresh`.\n *\n * The converse holds for THIS role: every RPC client depends on the minter, so a minter reads a\n * credential this process already holds (a paired worker token, an OAuth token store) and must not\n * itself inject a typed RPC client. Validating a credential server-side is the verifier's job, on\n * the server.\n *\n * ## It FAILS AT CLIENT CREATION\n *\n * `createRpcClient` refuses a contract with an endpoint whose only declared credential this client\n * could send is `vendorAuth(name)` when no minter is bound, or the bound minter's {@link supportedVendorNames}\n * does not list `name` — it throws `MissingClientCredentialError` naming the contract, the method and\n * the binding, before any call is made (#1362). An endpoint that also declares a credential this\n * client CAN send (OIDC, a held shared secret) needs no minter. There is no framework default and\n * there never will be one.\n *\n * ```typescript\n * // the CLI / client process's BindModule\n * binder.bind(VENDOR_AUTH_MINTER).to(MyVendorAuthMinter).inSingletonScope();\n *\n * class MyVendorAuthMinter extends VendorAuthMinter {\n * override supportedVendorNames(): readonly string[] {\n * return ['clover']; // every name mint()/refresh() below handle\n * }\n * // mint(...) and refresh(...)\n * }\n * ```\n *\n * ## Refresh-once, and who owns serializing it\n *\n * The node client's `OutboundAuthFilter` calls {@link refresh} ONCE when the vendor answers a raw\n * `401`, mints again and re-sends ONCE (never for a streaming upload, whose body cannot be replayed).\n * A second 401 is a failure of THIS service. Serializing a refresh across instances — Clover's\n * refresh tokens are single-use across Cloud Run instances — is the minter's job: webpieces calls it\n * once per logical call and cannot see the other instances. A refresh renews from its OWN valid\n * credential source; it never authenticates through the call it is refreshing for.\n */\nexport abstract class VendorAuthMinter {\n /**\n * Every vendor name {@link mint} and {@link refresh} handle — each `vendorAuth(name)` this process\n * can call. A METHOD beside `mint` so the list sits next to the code that dispatches on those\n * names: add a vendor to both in one edit. REQUIRED, no default.\n *\n * Called at CLIENT CREATION by `createRpcClient` (#1362), to refuse a contract it cannot call. It\n * MUST therefore be PURE — return a fixed list, with no I/O, no network, no credential work and no\n * side effect. {@link mint} and {@link refresh} are never called then.\n */\n abstract supportedVendorNames(): readonly string[];\n\n /**\n * Produce the headers that authenticate ONE outbound request, or throw to refuse to send it. An\n * empty map is legal and means \"this vendor needs no header\".\n *\n * @param name the string on the contract's `vendorAuth(name)` — which vendor this is.\n * @param request the FINAL request: the absolute url and the exact bytes about to go on the wire.\n */\n abstract mint(name: string, request: SignableRequest): Promise<Map<string, string>>;\n\n /**\n * The vendor answered `401` to a credential {@link mint} produced. Renew whatever the credential\n * is minted from (refresh an OAuth token, log in again). Called at most ONCE per logical call;\n * {@link mint} runs again afterwards and the request is re-sent once.\n */\n abstract refresh(name: string): Promise<void>;\n}\n\n/**\n * DI identifier for the optional {@link VendorAuthMinter} binding — the OUTBOUND role only. It is a\n * Symbol (not the class) so the app container's inversify autobind never auto-constructs this token,\n * keeping `@optional() @inject(VENDOR_AUTH_MINTER)` correct — undefined when unbound, which is what\n * lets `createRpcClient` refuse a contract this process cannot call.\n */\n// webpieces-disable no-symbol-di-tokens -- optional DI token: must be a Symbol so the app container's autobind never auto-constructs this token, keeping @optional() @inject(...) correct (undefined when unbound)\nexport const VENDOR_AUTH_MINTER = Symbol.for('VendorAuthMinter');\n"]}
|
|
@@ -21,6 +21,11 @@ export declare class VendorAuthRolesCompileAssertions {
|
|
|
21
21
|
minterIsNotAVerifier(): void;
|
|
22
22
|
/** A verifier-only class does not implement minting or refresh, so it cannot fill the outbound role. */
|
|
23
23
|
verifierIsNotAMinter(): void;
|
|
24
|
+
/**
|
|
25
|
+
* Each role DECLARES the vendor names it handles (#1362): a required abstract method with no
|
|
26
|
+
* default, because startup and `createRpcClient` call it to refuse a route nothing can serve.
|
|
27
|
+
*/
|
|
28
|
+
supportedVendorNamesIsRequired(): void;
|
|
24
29
|
/** The two binding identities are distinct unique symbols: one cannot be passed as the other. */
|
|
25
30
|
tokensAreNotInterchangeable(): void;
|
|
26
31
|
}
|
|
@@ -8,6 +8,9 @@ const VendorAuthMinter_1 = require("./VendorAuthMinter");
|
|
|
8
8
|
const VendorAuthVerifier_1 = require("./VendorAuthVerifier");
|
|
9
9
|
/** A CLIENT-only process's minter: mint + refresh, and NOTHING meaningless beside them. */
|
|
10
10
|
class MinterOnly extends VendorAuthMinter_1.VendorAuthMinter {
|
|
11
|
+
supportedVendorNames() {
|
|
12
|
+
return ['bugfixer-worker'];
|
|
13
|
+
}
|
|
11
14
|
async mint(_name, _request) {
|
|
12
15
|
return new Map([['Authorization', 'Bearer held-worker-token']]);
|
|
13
16
|
}
|
|
@@ -17,6 +20,9 @@ class MinterOnly extends VendorAuthMinter_1.VendorAuthMinter {
|
|
|
17
20
|
}
|
|
18
21
|
/** A SERVER-only process's verifier: verify, and NOTHING meaningless beside it. */
|
|
19
22
|
class VerifierOnly extends VendorAuthVerifier_1.VendorAuthVerifier {
|
|
23
|
+
supportedVendorNames() {
|
|
24
|
+
return ['bugfixer-sentry'];
|
|
25
|
+
}
|
|
20
26
|
async verify(_name, request) {
|
|
21
27
|
return new AuthenticatedCaller_1.AuthenticatedCaller(`vendor:${request.raw.rawBody.length}`);
|
|
22
28
|
}
|
|
@@ -56,6 +62,9 @@ class VendorAuthRolesCompileAssertions {
|
|
|
56
62
|
const verifier = new MinterOnly();
|
|
57
63
|
void verifier;
|
|
58
64
|
void class extends VendorAuthMinter_1.VendorAuthMinter {
|
|
65
|
+
supportedVendorNames() {
|
|
66
|
+
return ['x'];
|
|
67
|
+
}
|
|
59
68
|
async mint() {
|
|
60
69
|
return new Map();
|
|
61
70
|
}
|
|
@@ -74,6 +83,9 @@ class VendorAuthRolesCompileAssertions {
|
|
|
74
83
|
const minter = new VerifierOnly();
|
|
75
84
|
void minter;
|
|
76
85
|
void class extends VendorAuthVerifier_1.VendorAuthVerifier {
|
|
86
|
+
supportedVendorNames() {
|
|
87
|
+
return ['x'];
|
|
88
|
+
}
|
|
77
89
|
async verify() {
|
|
78
90
|
return new AuthenticatedCaller_1.AuthenticatedCaller('vendor');
|
|
79
91
|
}
|
|
@@ -83,6 +95,34 @@ class VendorAuthRolesCompileAssertions {
|
|
|
83
95
|
}
|
|
84
96
|
};
|
|
85
97
|
}
|
|
98
|
+
/**
|
|
99
|
+
* Each role DECLARES the vendor names it handles (#1362): a required abstract method with no
|
|
100
|
+
* default, because startup and `createRpcClient` call it to refuse a route nothing can serve.
|
|
101
|
+
*/
|
|
102
|
+
supportedVendorNamesIsRequired() {
|
|
103
|
+
// @ts-expect-error a minter must implement supportedVendorNames() — there is no default
|
|
104
|
+
void class extends VendorAuthMinter_1.VendorAuthMinter {
|
|
105
|
+
async mint() {
|
|
106
|
+
return new Map();
|
|
107
|
+
}
|
|
108
|
+
async refresh() {
|
|
109
|
+
// renew
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
// @ts-expect-error a verifier must implement supportedVendorNames() — there is no default
|
|
113
|
+
void class extends VendorAuthVerifier_1.VendorAuthVerifier {
|
|
114
|
+
async verify() {
|
|
115
|
+
return new AuthenticatedCaller_1.AuthenticatedCaller('vendor');
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
void class extends VendorAuthVerifier_1.VendorAuthVerifier {
|
|
119
|
+
// @ts-expect-error it is a METHOD beside verify(), not a field
|
|
120
|
+
supportedVendorNames = ['x'];
|
|
121
|
+
async verify() {
|
|
122
|
+
return new AuthenticatedCaller_1.AuthenticatedCaller('vendor');
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
}
|
|
86
126
|
/** The two binding identities are distinct unique symbols: one cannot be passed as the other. */
|
|
87
127
|
tokensAreNotInterchangeable() {
|
|
88
128
|
// @ts-expect-error the inbound token is not the outbound token
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VendorAuthRolesCompileAssertions.js","sourceRoot":"","sources":["../../../../../packages/core/core-context/src/VendorAuthRolesCompileAssertions.ts"],"names":[],"mappings":";;;;AAAA,+DAA4D;AAE5D,6DAAuC;AACvC,yDAA2F;AAC3F,6DAAgF;AAEhF,2FAA2F;AAC3F,MAAM,UAAW,SAAQ,mCAAgB;IAC5B,KAAK,CAAC,IAAI,CAAC,KAAa,EAAE,QAAyB;QACxD,OAAO,IAAI,GAAG,CAAC,CAAC,CAAC,eAAe,EAAE,0BAA0B,CAAC,CAAC,CAAC,CAAC;IACpE,CAAC;IAEQ,KAAK,CAAC,OAAO,CAAC,KAAa;QAChC,kDAAkD;IACtD,CAAC;CACJ;AAED,mFAAmF;AACnF,MAAM,YAAa,SAAQ,uCAAkB;IAChC,KAAK,CAAC,MAAM,CAAC,KAAa,EAAE,OAAuB;QACxD,OAAO,IAAI,yCAAmB,CAAC,UAAU,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC3E,CAAC;CACJ;AAED;;;;;;;;;;;;;GAaG;AACH,MAAa,gCAAgC;IACzC,uEAAuE;IACvE,oBAAoB;QAChB,qHAAqH;QACrH,KAAK,WAAW,CAAC,cAAc,CAAC;QAChC,uHAAuH;QACvH,KAAK,WAAW,CAAC,gBAAgB,CAAC;IACtC,CAAC;IAED,mGAAmG;IACnG,mBAAmB;QACf,MAAM,MAAM,GAAqB,IAAI,UAAU,EAAE,CAAC;QAClD,MAAM,QAAQ,GAAuB,IAAI,YAAY,EAAE,CAAC;QACxD,KAAK,MAAM,CAAC;QACZ,KAAK,QAAQ,CAAC;IAClB,CAAC;IAED,+FAA+F;IAC/F,oBAAoB;QAChB,4EAA4E;QAC5E,MAAM,QAAQ,GAAuB,IAAI,UAAU,EAAE,CAAC;QACtD,KAAK,QAAQ,CAAC;QACd,KAAK,KAAM,SAAQ,mCAAgB;YACtB,KAAK,CAAC,IAAI;gBACf,OAAO,IAAI,GAAG,EAAE,CAAC;YACrB,CAAC;YAEQ,KAAK,CAAC,OAAO;gBAClB,QAAQ;YACZ,CAAC;YAED,iGAAiG;YACxF,KAAK,CAAC,MAAM,CAAC,KAAa,EAAE,QAAwB;gBACzD,OAAO,IAAI,yCAAmB,CAAC,OAAO,CAAC,CAAC;YAC5C,CAAC;SACJ,CAAC;IACN,CAAC;IAED,wGAAwG;IACxG,oBAAoB;QAChB,oFAAoF;QACpF,MAAM,MAAM,GAAqB,IAAI,YAAY,EAAE,CAAC;QACpD,KAAK,MAAM,CAAC;QACZ,KAAK,KAAM,SAAQ,uCAAkB;YACxB,KAAK,CAAC,MAAM;gBACjB,OAAO,IAAI,yCAAmB,CAAC,QAAQ,CAAC,CAAC;YAC7C,CAAC;YAED,8FAA8F;YACrF,KAAK,CAAC,IAAI;gBACf,OAAO,IAAI,GAAG,EAAE,CAAC;YACrB,CAAC;SACJ,CAAC;IACN,CAAC;IAED,iGAAiG;IACjG,2BAA2B;QACvB,+DAA+D;QAC/D,MAAM,QAAQ,GAA8B,yCAAoB,CAAC;QACjE,+DAA+D;QAC/D,MAAM,OAAO,GAAgC,qCAAkB,CAAC;QAChE,KAAK,QAAQ,CAAC;QACd,KAAK,OAAO,CAAC;IACjB,CAAC;CACJ;
|
|
1
|
+
{"version":3,"file":"VendorAuthRolesCompileAssertions.js","sourceRoot":"","sources":["../../../../../packages/core/core-context/src/VendorAuthRolesCompileAssertions.ts"],"names":[],"mappings":"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","sourcesContent":["import { AuthenticatedCaller } from './AuthenticatedCaller';\nimport { RawHttpRequest } from './HttpRequest';\nimport * as CoreContext from './index';\nimport { SignableRequest, VendorAuthMinter, VENDOR_AUTH_MINTER } from './VendorAuthMinter';\nimport { VendorAuthVerifier, VENDOR_AUTH_VERIFIER } from './VendorAuthVerifier';\n\n/** A CLIENT-only process's minter: mint + refresh, and NOTHING meaningless beside them. */\nclass MinterOnly extends VendorAuthMinter {\n override supportedVendorNames(): readonly string[] {\n return ['bugfixer-worker'];\n }\n\n override async mint(_name: string, _request: SignableRequest): Promise<Map<string, string>> {\n return new Map([['Authorization', 'Bearer held-worker-token']]);\n }\n\n override async refresh(_name: string): Promise<void> {\n // renew from this process's own credential source\n }\n}\n\n/** A SERVER-only process's verifier: verify, and NOTHING meaningless beside it. */\nclass VerifierOnly extends VendorAuthVerifier {\n override supportedVendorNames(): readonly string[] {\n return ['bugfixer-sentry'];\n }\n\n override async verify(_name: string, request: RawHttpRequest): Promise<AuthenticatedCaller> {\n return new AuthenticatedCaller(`vendor:${request.raw.rawBody.length}`);\n }\n}\n\n/**\n * COMPILE-TIME proof of the #1358 role split. `vendorAuth(name)` is served by TWO independent DI\n * roles — the outbound {@link VendorAuthMinter} and the inbound {@link VendorAuthVerifier} — and the\n * unified `VendorAuthHook` / `VENDOR_AUTH_HOOK` they replace is DELETED, not aliased. Each\n * `@ts-expect-error` FAILS THE BUILD (TS2578) the moment the thing it guards starts compiling again.\n *\n * A compiled file, never a `.spec.ts`: tsconfig.lib.json excludes specs and vitest strips types with\n * esbuild, so a `@ts-expect-error` in a spec is inert.\n *\n * Why the split must be a TYPE fact and not a convention: the unified hook forced a client-only\n * process to implement a meaningless `verify` and a server-only one a meaningless `mint`/`refresh`,\n * and — far worse — made every outbound RPC proxy construct the server's inbound verifier, which\n * closed a DI cycle the moment the verifier depended on an ordinary OIDC RPC client.\n */\nexport class VendorAuthRolesCompileAssertions {\n /** The unified hook is gone: no alias, no `@deprecated`, by policy. */\n unifiedHookIsDeleted(): void {\n // @ts-expect-error VendorAuthHook is DELETED — split into VendorAuthMinter (outbound) + VendorAuthVerifier (inbound)\n void CoreContext.VendorAuthHook;\n // @ts-expect-error VENDOR_AUTH_HOOK is DELETED — bind VENDOR_AUTH_MINTER (client) and/or VENDOR_AUTH_VERIFIER (server)\n void CoreContext.VENDOR_AUTH_HOOK;\n }\n\n /** Each role is implementable ALONE — asserted by the absence of an error on the classes above. */\n eachRoleStandsAlone(): void {\n const minter: VendorAuthMinter = new MinterOnly();\n const verifier: VendorAuthVerifier = new VerifierOnly();\n void minter;\n void verifier;\n }\n\n /** A minter-only class does not implement verification, so it cannot fill the inbound role. */\n minterIsNotAVerifier(): void {\n // @ts-expect-error a minter has no verify(): it is not a VendorAuthVerifier\n const verifier: VendorAuthVerifier = new MinterOnly();\n void verifier;\n void class extends VendorAuthMinter {\n override supportedVendorNames(): readonly string[] {\n return ['x'];\n }\n\n override async mint(): Promise<Map<string, string>> {\n return new Map();\n }\n\n override async refresh(): Promise<void> {\n // renew\n }\n\n // @ts-expect-error verify is not a member of the outbound role, so it cannot be overridden there\n override async verify(_name: string, _request: RawHttpRequest): Promise<AuthenticatedCaller> {\n return new AuthenticatedCaller('never');\n }\n };\n }\n\n /** A verifier-only class does not implement minting or refresh, so it cannot fill the outbound role. */\n verifierIsNotAMinter(): void {\n // @ts-expect-error a verifier has no mint()/refresh(): it is not a VendorAuthMinter\n const minter: VendorAuthMinter = new VerifierOnly();\n void minter;\n void class extends VendorAuthVerifier {\n override supportedVendorNames(): readonly string[] {\n return ['x'];\n }\n\n override async verify(): Promise<AuthenticatedCaller> {\n return new AuthenticatedCaller('vendor');\n }\n\n // @ts-expect-error mint is not a member of the inbound role, so it cannot be overridden there\n override async mint(): Promise<Map<string, string>> {\n return new Map();\n }\n };\n }\n\n /**\n * Each role DECLARES the vendor names it handles (#1362): a required abstract method with no\n * default, because startup and `createRpcClient` call it to refuse a route nothing can serve.\n */\n supportedVendorNamesIsRequired(): void {\n // @ts-expect-error a minter must implement supportedVendorNames() — there is no default\n void class extends VendorAuthMinter {\n override async mint(): Promise<Map<string, string>> {\n return new Map();\n }\n\n override async refresh(): Promise<void> {\n // renew\n }\n };\n // @ts-expect-error a verifier must implement supportedVendorNames() — there is no default\n void class extends VendorAuthVerifier {\n override async verify(): Promise<AuthenticatedCaller> {\n return new AuthenticatedCaller('vendor');\n }\n };\n void class extends VendorAuthVerifier {\n // @ts-expect-error it is a METHOD beside verify(), not a field\n override readonly supportedVendorNames: readonly string[] = ['x'];\n\n override async verify(): Promise<AuthenticatedCaller> {\n return new AuthenticatedCaller('vendor');\n }\n };\n }\n\n /** The two binding identities are distinct unique symbols: one cannot be passed as the other. */\n tokensAreNotInterchangeable(): void {\n // @ts-expect-error the inbound token is not the outbound token\n const outbound: typeof VENDOR_AUTH_MINTER = VENDOR_AUTH_VERIFIER;\n // @ts-expect-error the outbound token is not the inbound token\n const inbound: typeof VENDOR_AUTH_VERIFIER = VENDOR_AUTH_MINTER;\n void outbound;\n void inbound;\n }\n}\n"]}
|
|
@@ -11,17 +11,36 @@ import { RawHttpRequest } from './HttpRequest';
|
|
|
11
11
|
* `oidcSameProject()` RPC client (to look a credential or a signing secret up) without closing a
|
|
12
12
|
* construction cycle (#1358).
|
|
13
13
|
*
|
|
14
|
-
* ## It FAILS
|
|
14
|
+
* ## It FAILS AT STARTUP
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
16
|
+
* A server that SERVES a `vendorAuth(name)` endpoint with no verifier bound, or with a verifier whose
|
|
17
|
+
* {@link supportedVendorNames} does not list `name`, refuses to boot: route registration names the endpoint,
|
|
18
|
+
* the vendor and the binding line (#1362). There is no framework default and there never will be
|
|
19
|
+
* one — never an allow-all.
|
|
18
20
|
*
|
|
19
21
|
* ```typescript
|
|
20
22
|
* // the API server's BindModule
|
|
21
23
|
* binder.bind(VENDOR_AUTH_VERIFIER).to(MyVendorAuthVerifier).inSingletonScope();
|
|
24
|
+
*
|
|
25
|
+
* class MyVendorAuthVerifier extends VendorAuthVerifier {
|
|
26
|
+
* override supportedVendorNames(): readonly string[] {
|
|
27
|
+
* return ['bugfixer-sentry']; // every name verify() below handles
|
|
28
|
+
* }
|
|
29
|
+
* override async verify(name: string, request: RawHttpRequest): Promise<AuthenticatedCaller> { ... }
|
|
30
|
+
* }
|
|
22
31
|
* ```
|
|
23
32
|
*/
|
|
24
33
|
export declare abstract class VendorAuthVerifier {
|
|
34
|
+
/**
|
|
35
|
+
* Every vendor name {@link verify} handles — each `vendorAuth(name)` this server can verify. A
|
|
36
|
+
* METHOD beside `verify` so the list sits next to the code that dispatches on those names: add a
|
|
37
|
+
* vendor to both in one edit. REQUIRED, no default.
|
|
38
|
+
*
|
|
39
|
+
* Called at STARTUP by route registration (#1362): a served `vendorAuth(name)` route whose name
|
|
40
|
+
* is not listed fails boot. It MUST therefore be PURE — return a fixed list, with no I/O, no
|
|
41
|
+
* network, no credential work and no side effect. {@link verify} is never called at boot.
|
|
42
|
+
*/
|
|
43
|
+
abstract supportedVendorNames(): readonly string[];
|
|
25
44
|
/**
|
|
26
45
|
* Verify ONE inbound request. Return the {@link AuthenticatedCaller} the vendor's credential
|
|
27
46
|
* proved; throw `ApiUnauthorizedError` to deny. The framework seeds `entries` with
|
|
@@ -39,6 +58,6 @@ export declare abstract class VendorAuthVerifier {
|
|
|
39
58
|
/**
|
|
40
59
|
* DI identifier for the optional {@link VendorAuthVerifier} binding — the INBOUND role only. A Symbol
|
|
41
60
|
* for the same reason as `VENDOR_AUTH_MINTER`: autobind never constructs it, so an unbound verifier
|
|
42
|
-
* is `undefined` and
|
|
61
|
+
* is `undefined` and route registration can refuse a served `vendorAuth(...)` endpoint at startup.
|
|
43
62
|
*/
|
|
44
63
|
export declare const VENDOR_AUTH_VERIFIER: unique symbol;
|
|
@@ -12,14 +12,23 @@ exports.VENDOR_AUTH_VERIFIER = exports.VendorAuthVerifier = void 0;
|
|
|
12
12
|
* `oidcSameProject()` RPC client (to look a credential or a signing secret up) without closing a
|
|
13
13
|
* construction cycle (#1358).
|
|
14
14
|
*
|
|
15
|
-
* ## It FAILS
|
|
15
|
+
* ## It FAILS AT STARTUP
|
|
16
16
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
17
|
+
* A server that SERVES a `vendorAuth(name)` endpoint with no verifier bound, or with a verifier whose
|
|
18
|
+
* {@link supportedVendorNames} does not list `name`, refuses to boot: route registration names the endpoint,
|
|
19
|
+
* the vendor and the binding line (#1362). There is no framework default and there never will be
|
|
20
|
+
* one — never an allow-all.
|
|
19
21
|
*
|
|
20
22
|
* ```typescript
|
|
21
23
|
* // the API server's BindModule
|
|
22
24
|
* binder.bind(VENDOR_AUTH_VERIFIER).to(MyVendorAuthVerifier).inSingletonScope();
|
|
25
|
+
*
|
|
26
|
+
* class MyVendorAuthVerifier extends VendorAuthVerifier {
|
|
27
|
+
* override supportedVendorNames(): readonly string[] {
|
|
28
|
+
* return ['bugfixer-sentry']; // every name verify() below handles
|
|
29
|
+
* }
|
|
30
|
+
* override async verify(name: string, request: RawHttpRequest): Promise<AuthenticatedCaller> { ... }
|
|
31
|
+
* }
|
|
23
32
|
* ```
|
|
24
33
|
*/
|
|
25
34
|
class VendorAuthVerifier {
|
|
@@ -28,7 +37,7 @@ exports.VendorAuthVerifier = VendorAuthVerifier;
|
|
|
28
37
|
/**
|
|
29
38
|
* DI identifier for the optional {@link VendorAuthVerifier} binding — the INBOUND role only. A Symbol
|
|
30
39
|
* for the same reason as `VENDOR_AUTH_MINTER`: autobind never constructs it, so an unbound verifier
|
|
31
|
-
* is `undefined` and
|
|
40
|
+
* is `undefined` and route registration can refuse a served `vendorAuth(...)` endpoint at startup.
|
|
32
41
|
*/
|
|
33
42
|
// webpieces-disable no-symbol-di-tokens -- optional DI token: must be a Symbol so the app container's autobind never auto-constructs this token, keeping @optional() @inject(...) correct (undefined when unbound)
|
|
34
43
|
exports.VENDOR_AUTH_VERIFIER = Symbol.for('VendorAuthVerifier');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VendorAuthVerifier.js","sourceRoot":"","sources":["../../../../../packages/core/core-context/src/VendorAuthVerifier.ts"],"names":[],"mappings":";;;AAGA
|
|
1
|
+
{"version":3,"file":"VendorAuthVerifier.js","sourceRoot":"","sources":["../../../../../packages/core/core-context/src/VendorAuthVerifier.ts"],"names":[],"mappings":";;;AAGA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAsB,kBAAkB;CAyBvC;AAzBD,gDAyBC;AAED;;;;GAIG;AACH,mNAAmN;AACtM,QAAA,oBAAoB,GAAG,MAAM,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC","sourcesContent":["import { AuthenticatedCaller } from './AuthenticatedCaller';\nimport { RawHttpRequest } from './HttpRequest';\n\n/**\n * VendorAuthVerifier - the INBOUND half of `vendorAuth(name)`: the app-bound mechanism that checks a\n * credential in a scheme the FRAMEWORK does not know when a vendor / partner calls THIS process.\n * `name` selects the vendor, so one verifier serves every vendor this server receives.\n *\n * It is one of TWO independent, optional DI roles behind the one contract declaration\n * `vendorAuth(name)`; its outbound twin is `VendorAuthMinter`. Only the server's `AuthFilter`\n * depends on this role. No outbound RPC client does — so a verifier may itself depend on an ordinary\n * `oidcSameProject()` RPC client (to look a credential or a signing secret up) without closing a\n * construction cycle (#1358).\n *\n * ## It FAILS AT STARTUP\n *\n * A server that SERVES a `vendorAuth(name)` endpoint with no verifier bound, or with a verifier whose\n * {@link supportedVendorNames} does not list `name`, refuses to boot: route registration names the endpoint,\n * the vendor and the binding line (#1362). There is no framework default and there never will be\n * one — never an allow-all.\n *\n * ```typescript\n * // the API server's BindModule\n * binder.bind(VENDOR_AUTH_VERIFIER).to(MyVendorAuthVerifier).inSingletonScope();\n *\n * class MyVendorAuthVerifier extends VendorAuthVerifier {\n * override supportedVendorNames(): readonly string[] {\n * return ['bugfixer-sentry']; // every name verify() below handles\n * }\n * override async verify(name: string, request: RawHttpRequest): Promise<AuthenticatedCaller> { ... }\n * }\n * ```\n */\nexport abstract class VendorAuthVerifier {\n /**\n * Every vendor name {@link verify} handles — each `vendorAuth(name)` this server can verify. A\n * METHOD beside `verify` so the list sits next to the code that dispatches on those names: add a\n * vendor to both in one edit. REQUIRED, no default.\n *\n * Called at STARTUP by route registration (#1362): a served `vendorAuth(name)` route whose name\n * is not listed fails boot. It MUST therefore be PURE — return a fixed list, with no I/O, no\n * network, no credential work and no side effect. {@link verify} is never called at boot.\n */\n abstract supportedVendorNames(): readonly string[];\n\n /**\n * Verify ONE inbound request. Return the {@link AuthenticatedCaller} the vendor's credential\n * proved; throw `ApiUnauthorizedError` to deny. The framework seeds `entries` with\n * `RequestContext.putTrusted`, as it does for a jwt or api-key caller. `vendorAuth` stays\n * caller-NOT-verified for trusted-header purposes: a vendor is not a peer service, so nothing it\n * merely ASSERTS on the wire is admitted.\n *\n * @param name the string on the contract's `vendorAuth(name)` — which vendor this route is.\n * @param request narrowed to {@link RawHttpRequest}: `request.raw` (the verbatim bytes + absolute\n * url) is PRESENT, because a served `vendorAuth(...)` endpoint requires\n * `@Endpoint(..., { rawBody: true })` at wiring time.\n */\n abstract verify(name: string, request: RawHttpRequest): Promise<AuthenticatedCaller>;\n}\n\n/**\n * DI identifier for the optional {@link VendorAuthVerifier} binding — the INBOUND role only. A Symbol\n * for the same reason as `VENDOR_AUTH_MINTER`: autobind never constructs it, so an unbound verifier\n * is `undefined` and route registration can refuse a served `vendorAuth(...)` endpoint at startup.\n */\n// webpieces-disable no-symbol-di-tokens -- optional DI token: must be a Symbol so the app container's autobind never auto-constructs this token, keeping @optional() @inject(...) correct (undefined when unbound)\nexport const VENDOR_AUTH_VERIFIER = Symbol.for('VendorAuthVerifier');\n"]}
|