@webpieces/core-util 0.4.880 → 0.4.881
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 +1 -1
- package/src/errors/ApiCallSite.d.ts +58 -0
- package/src/errors/ApiCallSite.js +96 -0
- package/src/errors/ApiCallSite.js.map +1 -0
- package/src/errors/ApiError.d.ts +9 -0
- package/src/errors/ApiError.js.map +1 -1
- package/src/errors/ReportableApiFailure.d.ts +43 -0
- package/src/errors/ReportableApiFailure.js +75 -0
- package/src/errors/ReportableApiFailure.js.map +1 -0
- package/src/errors/index.d.ts +3 -0
- package/src/errors/index.js +6 -1
- package/src/errors/index.js.map +1 -1
- package/src/http/WebpiecesDefaultErrorTranslator.d.ts +3 -0
- package/src/http/WebpiecesDefaultErrorTranslator.js +10 -0
- package/src/http/WebpiecesDefaultErrorTranslator.js.map +1 -1
- package/src/http/WebpiecesDefaultFailureClassifier.d.ts +3 -2
- package/src/http/WebpiecesDefaultFailureClassifier.js +6 -3
- package/src/http/WebpiecesDefaultFailureClassifier.js.map +1 -1
- package/src/index.d.ts +8 -2
- package/src/index.js +20 -1
- package/src/index.js.map +1 -1
- package/src/oauth/OAuthAuthorizationErrorRedirect.d.ts +37 -0
- package/src/oauth/OAuthAuthorizationErrorRedirect.js +61 -0
- package/src/oauth/OAuthAuthorizationErrorRedirect.js.map +1 -0
- package/src/oauth/OAuthBearerChallenge.d.ts +31 -0
- package/src/oauth/OAuthBearerChallenge.js +52 -0
- package/src/oauth/OAuthBearerChallenge.js.map +1 -0
- package/src/oauth/OAuthErrorCode.d.ts +69 -0
- package/src/oauth/OAuthErrorCode.js +89 -0
- package/src/oauth/OAuthErrorCode.js.map +1 -0
- package/src/oauth/OAuthErrorResponse.d.ts +34 -0
- package/src/oauth/OAuthErrorResponse.js +74 -0
- package/src/oauth/OAuthErrorResponse.js.map +1 -0
- package/src/oauth/OAuthProtocolError.d.ts +61 -0
- package/src/oauth/OAuthProtocolError.js +83 -0
- package/src/oauth/OAuthProtocolError.js.map +1 -0
- package/src/oauth/OAuthProtocolErrorCompileAssertions.d.ts +10 -0
- package/src/oauth/OAuthProtocolErrorCompileAssertions.js +39 -0
- package/src/oauth/OAuthProtocolErrorCompileAssertions.js.map +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OAuthErrorCode.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/oauth/OAuthErrorCode.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,IAAY,cAiBX;AAjBD,WAAY,cAAc;IACtB,qDAAmC,CAAA;IACnC,mDAAiC,CAAA;IACjC,iDAA+B,CAAA;IAC/B,6DAA2C,CAAA;IAC3C,mEAAiD,CAAA;IACjD,iDAA+B,CAAA;IAC/B,iDAA+B,CAAA;IAC/B,2DAAyC,CAAA;IACzC,iDAA+B,CAAA;IAC/B,yEAAuD,CAAA;IACvD,+CAA6B,CAAA;IAC7B,qEAAmD,CAAA;IACnD,+DAA6C,CAAA;IAC7C,qEAAmD,CAAA;IACnD,2EAAyD,CAAA;IACzD,iFAA+D,CAAA;AACnE,CAAC,EAjBW,cAAc,8BAAd,cAAc,QAiBzB;AA+BD;;;;;GAKG;AACH,MAAa,gBAAgB;IACzB,uFAAuF;IACvF,MAAM,CAAC,EAAE,CAAC,IAA+B;QACrC,QAAQ,IAAI,EAAE,CAAC;YACX,KAAK,cAAc,CAAC,aAAa;gBAC7B,OAAO,GAAG,CAAC;YACf,KAAK,cAAc,CAAC,kBAAkB,CAAC;YACvC,KAAK,cAAc,CAAC,aAAa;gBAC7B,OAAO,GAAG,CAAC;YACf,KAAK,cAAc,CAAC,YAAY;gBAC5B,OAAO,GAAG,CAAC;YACf,KAAK,cAAc,CAAC,uBAAuB;gBACvC,OAAO,GAAG,CAAC;YACf,KAAK,cAAc,CAAC,eAAe,CAAC;YACpC,KAAK,cAAc,CAAC,aAAa,CAAC;YAClC,KAAK,cAAc,CAAC,mBAAmB,CAAC;YACxC,KAAK,cAAc,CAAC,sBAAsB,CAAC;YAC3C,KAAK,cAAc,CAAC,aAAa,CAAC;YAClC,KAAK,cAAc,CAAC,yBAAyB,CAAC;YAC9C,KAAK,cAAc,CAAC,oBAAoB,CAAC;YACzC,KAAK,cAAc,CAAC,uBAAuB,CAAC;YAC5C,KAAK,cAAc,CAAC,0BAA0B,CAAC;YAC/C,KAAK,cAAc,CAAC,6BAA6B;gBAC7C,OAAO,GAAG,CAAC;QACnB,CAAC;IACL,CAAC;CACJ;AA1BD,4CA0BC;AAED;;;;;GAKG;AACH,MAAa,SAAS;IAClB,0GAA0G;IAC1G,MAAM,CAAC,QAAQ,CAAC,KAAa;QACzB,OAAO,KAAK,CAAC,OAAO,CAAC,iCAAiC,EAAE,GAAG,CAAC,CAAC;IACjE,CAAC;CACJ;AALD,8BAKC","sourcesContent":["/**\n * The fixed OAuth 2.x error vocabulary. A throw site picks a member; it cannot invent a code (an\n * `invalid_credential` that no OAuth client understands) because the type has no such member.\n *\n * Sources, by the endpoint that answers with them:\n * - RFC 6749 §5.2, the TOKEN endpoint: `invalid_request`, `invalid_client`, `invalid_grant`,\n * `unauthorized_client`, `unsupported_grant_type`, `invalid_scope`.\n * - RFC 6750 §3.1, a PROTECTED RESOURCE (bearer token): `invalid_request`, `invalid_token`,\n * `insufficient_scope`.\n * - RFC 6749 §4.1.2.1, the AUTHORIZATION endpoint (answered as a redirect, see\n * `OAuthAuthorizationErrorRedirect`): `invalid_request`, `unauthorized_client`, `access_denied`,\n * `unsupported_response_type`, `invalid_scope`, `server_error`, `temporarily_unavailable`.\n * - RFC 7591 §3.2.2, DYNAMIC CLIENT REGISTRATION (which MCP clients use to register themselves):\n * `invalid_redirect_uri`, `invalid_client_metadata`, `invalid_software_statement`,\n * `unapproved_software_statement`.\n *\n * A string enum, every member initialised with its wire value, per the one-spelling rule for API\n * vocabularies in this repo.\n */\nexport enum OAuthErrorCode {\n INVALID_REQUEST = 'invalid_request',\n INVALID_CLIENT = 'invalid_client',\n INVALID_GRANT = 'invalid_grant',\n UNAUTHORIZED_CLIENT = 'unauthorized_client',\n UNSUPPORTED_GRANT_TYPE = 'unsupported_grant_type',\n INVALID_SCOPE = 'invalid_scope',\n INVALID_TOKEN = 'invalid_token',\n INSUFFICIENT_SCOPE = 'insufficient_scope',\n ACCESS_DENIED = 'access_denied',\n UNSUPPORTED_RESPONSE_TYPE = 'unsupported_response_type',\n SERVER_ERROR = 'server_error',\n TEMPORARILY_UNAVAILABLE = 'temporarily_unavailable',\n INVALID_REDIRECT_URI = 'invalid_redirect_uri',\n INVALID_CLIENT_METADATA = 'invalid_client_metadata',\n INVALID_SOFTWARE_STATEMENT = 'invalid_software_statement',\n UNAPPROVED_SOFTWARE_STATEMENT = 'unapproved_software_statement',\n}\n\n/** Every status an OAuth error is answered with when it is rendered as JSON. */\nexport type OAuthHttpStatus = 400 | 401 | 403 | 500 | 503;\n\n/**\n * The two statuses RFC 6749 §5.2 permits for `invalid_client`: 401 by default, and REQUIRED (with a\n * `WWW-Authenticate` header) when the client authenticated through the `Authorization` header; 400\n * is allowed otherwise. It is the ONLY code whose status the RFCs leave to the server.\n */\nexport type InvalidClientStatus = 400 | 401;\n\n/** Every code whose status is fixed by the RFCs — all of them except `invalid_client`. */\nexport type OAuthFixedStatusErrorCode = Exclude<OAuthErrorCode, OAuthErrorCode.INVALID_CLIENT>;\n\n/** The codes RFC 6750 §3.1 defines for a `WWW-Authenticate: Bearer` challenge. */\nexport type BearerErrorCode =\n | OAuthErrorCode.INVALID_REQUEST\n | OAuthErrorCode.INVALID_TOKEN\n | OAuthErrorCode.INSUFFICIENT_SCOPE;\n\n/** The codes RFC 6749 §4.1.2.1 defines for the authorization endpoint's error redirect. */\nexport type AuthorizationEndpointErrorCode =\n | OAuthErrorCode.INVALID_REQUEST\n | OAuthErrorCode.UNAUTHORIZED_CLIENT\n | OAuthErrorCode.ACCESS_DENIED\n | OAuthErrorCode.UNSUPPORTED_RESPONSE_TYPE\n | OAuthErrorCode.INVALID_SCOPE\n | OAuthErrorCode.SERVER_ERROR\n | OAuthErrorCode.TEMPORARILY_UNAVAILABLE;\n\n/**\n * The status each fixed code is answered with. The RFCs fix the token-endpoint and bearer codes;\n * the authorization-endpoint codes are normally a REDIRECT, not a status, so their status here is\n * only used when one is rendered as JSON (an authorization request whose `redirect_uri` could not be\n * trusted, where RFC 6749 §4.1.2.1 forbids redirecting): the closest HTTP meaning of each.\n */\nexport class OAuthErrorStatus {\n // webpieces-disable no-function-outside-class -- pure, exhaustive code -> status table\n static of(code: OAuthFixedStatusErrorCode): OAuthHttpStatus {\n switch (code) {\n case OAuthErrorCode.INVALID_TOKEN:\n return 401;\n case OAuthErrorCode.INSUFFICIENT_SCOPE:\n case OAuthErrorCode.ACCESS_DENIED:\n return 403;\n case OAuthErrorCode.SERVER_ERROR:\n return 500;\n case OAuthErrorCode.TEMPORARILY_UNAVAILABLE:\n return 503;\n case OAuthErrorCode.INVALID_REQUEST:\n case OAuthErrorCode.INVALID_GRANT:\n case OAuthErrorCode.UNAUTHORIZED_CLIENT:\n case OAuthErrorCode.UNSUPPORTED_GRANT_TYPE:\n case OAuthErrorCode.INVALID_SCOPE:\n case OAuthErrorCode.UNSUPPORTED_RESPONSE_TYPE:\n case OAuthErrorCode.INVALID_REDIRECT_URI:\n case OAuthErrorCode.INVALID_CLIENT_METADATA:\n case OAuthErrorCode.INVALID_SOFTWARE_STATEMENT:\n case OAuthErrorCode.UNAPPROVED_SOFTWARE_STATEMENT:\n return 400;\n }\n }\n}\n\n/**\n * RFC 6749 restricts `error_description` (and RFC 6750 every quoted challenge attribute) to\n * printable ASCII without `\"` and `\\` (%x20-21 / %x23-5B / %x5D-7E). Anything else is replaced with\n * `?` at RENDER time — the error itself keeps the original text for the operator log — so an app's\n * description can never break a header or the RFC's character set.\n */\nexport class OAuthText {\n // webpieces-disable no-function-outside-class -- pure character-set filter shared by every OAuth renderer\n static sanitize(value: string): string {\n return value.replace(/[^\\x20-\\x21\\x23-\\x5B\\x5D-\\x7E]/g, '?');\n }\n}\n"]}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { HttpResponseDto } from '../http/HttpResponseDto';
|
|
2
|
+
import { OAuthErrorCode } from './OAuthErrorCode';
|
|
3
|
+
import { OAuthProtocolError } from './OAuthProtocolError';
|
|
4
|
+
/**
|
|
5
|
+
* The RFC 6749 §5.2 / RFC 6750 §3 error body: `{ "error": "...", "error_description"?: "...",
|
|
6
|
+
* "error_uri"?: "..." }`. The property names ARE the wire names, snake_case, because an OAuth client
|
|
7
|
+
* parses exactly these keys. An absent optional is left off the JSON entirely.
|
|
8
|
+
*/
|
|
9
|
+
export declare class OAuthErrorBody {
|
|
10
|
+
readonly error: OAuthErrorCode;
|
|
11
|
+
readonly error_description?: string;
|
|
12
|
+
readonly error_uri?: string;
|
|
13
|
+
constructor(error: OAuthErrorCode, description?: string, errorUri?: string);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* {@link OAuthProtocolError} -> the exact HTTP response. ONE renderer, used by every boundary that
|
|
17
|
+
* can meet one (`WebpiecesDefaultErrorTranslator`, `OAuthExpressErrorHandler`, the MCP bearer
|
|
18
|
+
* boundary), so an OAuth client gets the same bytes whichever path the refusal took.
|
|
19
|
+
*
|
|
20
|
+
* Headers: the error's `WWW-Authenticate` when it has one; otherwise `defaultChallenge` for a 401
|
|
21
|
+
* (the MCP boundary passes its own challenge, which carries `resource_metadata`); otherwise, for
|
|
22
|
+
* `invalid_token` / `insufficient_scope`, the minimal Bearer challenge RFC 6750 §3 requires. Every
|
|
23
|
+
* response also carries `Cache-Control: no-store` and `Pragma: no-cache` (RFC 6749 §5.1), so no
|
|
24
|
+
* intermediary caches an answer about a credential.
|
|
25
|
+
*/
|
|
26
|
+
export declare class OAuthErrorResponse {
|
|
27
|
+
private static readonly REASONS;
|
|
28
|
+
/**
|
|
29
|
+
* @param defaultChallenge - the boundary's own `WWW-Authenticate` for a 401 whose error carries
|
|
30
|
+
* none, or `undefined` when the boundary has no challenge of its own.
|
|
31
|
+
*/
|
|
32
|
+
static toResponse(error: OAuthProtocolError, defaultChallenge: string | undefined): HttpResponseDto<OAuthErrorBody>;
|
|
33
|
+
private static challengeFor;
|
|
34
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OAuthErrorResponse = exports.OAuthErrorBody = void 0;
|
|
4
|
+
const HttpResponseDto_1 = require("../http/HttpResponseDto");
|
|
5
|
+
const OAuthBearerChallenge_1 = require("./OAuthBearerChallenge");
|
|
6
|
+
const OAuthErrorCode_1 = require("./OAuthErrorCode");
|
|
7
|
+
/**
|
|
8
|
+
* The RFC 6749 §5.2 / RFC 6750 §3 error body: `{ "error": "...", "error_description"?: "...",
|
|
9
|
+
* "error_uri"?: "..." }`. The property names ARE the wire names, snake_case, because an OAuth client
|
|
10
|
+
* parses exactly these keys. An absent optional is left off the JSON entirely.
|
|
11
|
+
*/
|
|
12
|
+
class OAuthErrorBody {
|
|
13
|
+
error;
|
|
14
|
+
error_description;
|
|
15
|
+
error_uri;
|
|
16
|
+
constructor(error, description, errorUri) {
|
|
17
|
+
this.error = error;
|
|
18
|
+
if (description !== undefined)
|
|
19
|
+
this.error_description = OAuthErrorCode_1.OAuthText.sanitize(description);
|
|
20
|
+
if (errorUri !== undefined)
|
|
21
|
+
this.error_uri = errorUri;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
exports.OAuthErrorBody = OAuthErrorBody;
|
|
25
|
+
/**
|
|
26
|
+
* {@link OAuthProtocolError} -> the exact HTTP response. ONE renderer, used by every boundary that
|
|
27
|
+
* can meet one (`WebpiecesDefaultErrorTranslator`, `OAuthExpressErrorHandler`, the MCP bearer
|
|
28
|
+
* boundary), so an OAuth client gets the same bytes whichever path the refusal took.
|
|
29
|
+
*
|
|
30
|
+
* Headers: the error's `WWW-Authenticate` when it has one; otherwise `defaultChallenge` for a 401
|
|
31
|
+
* (the MCP boundary passes its own challenge, which carries `resource_metadata`); otherwise, for
|
|
32
|
+
* `invalid_token` / `insufficient_scope`, the minimal Bearer challenge RFC 6750 §3 requires. Every
|
|
33
|
+
* response also carries `Cache-Control: no-store` and `Pragma: no-cache` (RFC 6749 §5.1), so no
|
|
34
|
+
* intermediary caches an answer about a credential.
|
|
35
|
+
*/
|
|
36
|
+
class OAuthErrorResponse {
|
|
37
|
+
static REASONS = new Map([
|
|
38
|
+
[400, 'Bad Request'],
|
|
39
|
+
[401, 'Unauthorized'],
|
|
40
|
+
[403, 'Forbidden'],
|
|
41
|
+
[500, 'Internal Server Error'],
|
|
42
|
+
[503, 'Service Unavailable'],
|
|
43
|
+
]);
|
|
44
|
+
/**
|
|
45
|
+
* @param defaultChallenge - the boundary's own `WWW-Authenticate` for a 401 whose error carries
|
|
46
|
+
* none, or `undefined` when the boundary has no challenge of its own.
|
|
47
|
+
*/
|
|
48
|
+
// webpieces-disable no-function-outside-class -- pure error -> response mapping shared by every boundary
|
|
49
|
+
static toResponse(error, defaultChallenge) {
|
|
50
|
+
const headers = [
|
|
51
|
+
new HttpResponseDto_1.HttpHeader('Cache-Control', 'no-store'),
|
|
52
|
+
new HttpResponseDto_1.HttpHeader('Pragma', 'no-cache'),
|
|
53
|
+
];
|
|
54
|
+
const challenge = OAuthErrorResponse.challengeFor(error, defaultChallenge);
|
|
55
|
+
if (challenge !== undefined) {
|
|
56
|
+
headers.push(new HttpResponseDto_1.HttpHeader('WWW-Authenticate', challenge));
|
|
57
|
+
}
|
|
58
|
+
return new HttpResponseDto_1.HttpResponseDto(new HttpResponseDto_1.HttpResponseStatus(error.status, OAuthErrorResponse.REASONS.get(error.status) ?? ''), headers, new OAuthErrorBody(error.error, error.description, error.errorUri));
|
|
59
|
+
}
|
|
60
|
+
// webpieces-disable no-function-outside-class -- pure header choice owned by this renderer
|
|
61
|
+
static challengeFor(error, defaultChallenge) {
|
|
62
|
+
if (error.wwwAuthenticate !== undefined)
|
|
63
|
+
return error.wwwAuthenticate;
|
|
64
|
+
if (error.status === 401 && defaultChallenge !== undefined)
|
|
65
|
+
return defaultChallenge;
|
|
66
|
+
if (error.error === OAuthErrorCode_1.OAuthErrorCode.INVALID_TOKEN ||
|
|
67
|
+
error.error === OAuthErrorCode_1.OAuthErrorCode.INSUFFICIENT_SCOPE) {
|
|
68
|
+
return new OAuthBearerChallenge_1.OAuthBearerChallenge(error.error, error.description).toHeaderValue();
|
|
69
|
+
}
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
exports.OAuthErrorResponse = OAuthErrorResponse;
|
|
74
|
+
//# sourceMappingURL=OAuthErrorResponse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OAuthErrorResponse.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/oauth/OAuthErrorResponse.ts"],"names":[],"mappings":";;;AAAA,6DAA0F;AAC1F,iEAA8D;AAC9D,qDAA8E;AAG9E;;;;GAIG;AACH,MAAa,cAAc;IACd,KAAK,CAAiB;IACtB,iBAAiB,CAAU;IAC3B,SAAS,CAAU;IAE5B,YAAY,KAAqB,EAAE,WAAoB,EAAE,QAAiB;QACtE,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,WAAW,KAAK,SAAS;YAAE,IAAI,CAAC,iBAAiB,GAAG,0BAAS,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QACxF,IAAI,QAAQ,KAAK,SAAS;YAAE,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAC;IAC1D,CAAC;CACJ;AAVD,wCAUC;AAED;;;;;;;;;;GAUG;AACH,MAAa,kBAAkB;IACnB,MAAM,CAAU,OAAO,GAAyC,IAAI,GAAG,CAG7E;QACE,CAAC,GAAG,EAAE,aAAa,CAAC;QACpB,CAAC,GAAG,EAAE,cAAc,CAAC;QACrB,CAAC,GAAG,EAAE,WAAW,CAAC;QAClB,CAAC,GAAG,EAAE,uBAAuB,CAAC;QAC9B,CAAC,GAAG,EAAE,qBAAqB,CAAC;KAC/B,CAAC,CAAC;IAEH;;;OAGG;IACH,yGAAyG;IACzG,MAAM,CAAC,UAAU,CACb,KAAyB,EACzB,gBAAoC;QAEpC,MAAM,OAAO,GAAG;YACZ,IAAI,4BAAU,CAAC,eAAe,EAAE,UAAU,CAAC;YAC3C,IAAI,4BAAU,CAAC,QAAQ,EAAE,UAAU,CAAC;SACvC,CAAC;QACF,MAAM,SAAS,GAAG,kBAAkB,CAAC,YAAY,CAAC,KAAK,EAAE,gBAAgB,CAAC,CAAC;QAC3E,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;YAC1B,OAAO,CAAC,IAAI,CAAC,IAAI,4BAAU,CAAC,kBAAkB,EAAE,SAAS,CAAC,CAAC,CAAC;QAChE,CAAC;QACD,OAAO,IAAI,iCAAe,CACtB,IAAI,oCAAkB,CAClB,KAAK,CAAC,MAAM,EACZ,kBAAkB,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,CACrD,EACD,OAAO,EACP,IAAI,cAAc,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,CACrE,CAAC;IACN,CAAC;IAED,2FAA2F;IACnF,MAAM,CAAC,YAAY,CACvB,KAAyB,EACzB,gBAAoC;QAEpC,IAAI,KAAK,CAAC,eAAe,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC,eAAe,CAAC;QACtE,IAAI,KAAK,CAAC,MAAM,KAAK,GAAG,IAAI,gBAAgB,KAAK,SAAS;YAAE,OAAO,gBAAgB,CAAC;QACpF,IACI,KAAK,CAAC,KAAK,KAAK,+BAAc,CAAC,aAAa;YAC5C,KAAK,CAAC,KAAK,KAAK,+BAAc,CAAC,kBAAkB,EACnD,CAAC;YACC,OAAO,IAAI,2CAAoB,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC,aAAa,EAAE,CAAC;QACpF,CAAC;QACD,OAAO,SAAS,CAAC;IACrB,CAAC;;AArDL,gDAsDC","sourcesContent":["import { HttpHeader, HttpResponseDto, HttpResponseStatus } from '../http/HttpResponseDto';\nimport { OAuthBearerChallenge } from './OAuthBearerChallenge';\nimport { OAuthErrorCode, OAuthHttpStatus, OAuthText } from './OAuthErrorCode';\nimport { OAuthProtocolError } from './OAuthProtocolError';\n\n/**\n * The RFC 6749 §5.2 / RFC 6750 §3 error body: `{ \"error\": \"...\", \"error_description\"?: \"...\",\n * \"error_uri\"?: \"...\" }`. The property names ARE the wire names, snake_case, because an OAuth client\n * parses exactly these keys. An absent optional is left off the JSON entirely.\n */\nexport class OAuthErrorBody {\n readonly error: OAuthErrorCode;\n readonly error_description?: string;\n readonly error_uri?: string;\n\n constructor(error: OAuthErrorCode, description?: string, errorUri?: string) {\n this.error = error;\n if (description !== undefined) this.error_description = OAuthText.sanitize(description);\n if (errorUri !== undefined) this.error_uri = errorUri;\n }\n}\n\n/**\n * {@link OAuthProtocolError} -> the exact HTTP response. ONE renderer, used by every boundary that\n * can meet one (`WebpiecesDefaultErrorTranslator`, `OAuthExpressErrorHandler`, the MCP bearer\n * boundary), so an OAuth client gets the same bytes whichever path the refusal took.\n *\n * Headers: the error's `WWW-Authenticate` when it has one; otherwise `defaultChallenge` for a 401\n * (the MCP boundary passes its own challenge, which carries `resource_metadata`); otherwise, for\n * `invalid_token` / `insufficient_scope`, the minimal Bearer challenge RFC 6750 §3 requires. Every\n * response also carries `Cache-Control: no-store` and `Pragma: no-cache` (RFC 6749 §5.1), so no\n * intermediary caches an answer about a credential.\n */\nexport class OAuthErrorResponse {\n private static readonly REASONS: ReadonlyMap<OAuthHttpStatus, string> = new Map<\n OAuthHttpStatus,\n string\n >([\n [400, 'Bad Request'],\n [401, 'Unauthorized'],\n [403, 'Forbidden'],\n [500, 'Internal Server Error'],\n [503, 'Service Unavailable'],\n ]);\n\n /**\n * @param defaultChallenge - the boundary's own `WWW-Authenticate` for a 401 whose error carries\n * none, or `undefined` when the boundary has no challenge of its own.\n */\n // webpieces-disable no-function-outside-class -- pure error -> response mapping shared by every boundary\n static toResponse(\n error: OAuthProtocolError,\n defaultChallenge: string | undefined,\n ): HttpResponseDto<OAuthErrorBody> {\n const headers = [\n new HttpHeader('Cache-Control', 'no-store'),\n new HttpHeader('Pragma', 'no-cache'),\n ];\n const challenge = OAuthErrorResponse.challengeFor(error, defaultChallenge);\n if (challenge !== undefined) {\n headers.push(new HttpHeader('WWW-Authenticate', challenge));\n }\n return new HttpResponseDto(\n new HttpResponseStatus(\n error.status,\n OAuthErrorResponse.REASONS.get(error.status) ?? '',\n ),\n headers,\n new OAuthErrorBody(error.error, error.description, error.errorUri),\n );\n }\n\n // webpieces-disable no-function-outside-class -- pure header choice owned by this renderer\n private static challengeFor(\n error: OAuthProtocolError,\n defaultChallenge: string | undefined,\n ): string | undefined {\n if (error.wwwAuthenticate !== undefined) return error.wwwAuthenticate;\n if (error.status === 401 && defaultChallenge !== undefined) return defaultChallenge;\n if (\n error.error === OAuthErrorCode.INVALID_TOKEN ||\n error.error === OAuthErrorCode.INSUFFICIENT_SCOPE\n ) {\n return new OAuthBearerChallenge(error.error, error.description).toHeaderValue();\n }\n return undefined;\n }\n}\n"]}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { InvalidClientStatus, OAuthErrorCode, OAuthFixedStatusErrorCode, OAuthHttpStatus } from './OAuthErrorCode';
|
|
2
|
+
/**
|
|
3
|
+
* The optional RFC extras of an {@link OAuthProtocolError}. Data only, so a class.
|
|
4
|
+
*
|
|
5
|
+
* - `errorUri` — RFC 6749 `error_uri`: a page a client DEVELOPER can read about the error.
|
|
6
|
+
* - `wwwAuthenticate` — the `WWW-Authenticate` value to send. Build the Bearer form with
|
|
7
|
+
* `OAuthBearerChallenge`. When it is omitted for `invalid_token` / `insufficient_scope`, the
|
|
8
|
+
* renderer derives the minimal `Bearer error="..."` challenge RFC 6750 requires.
|
|
9
|
+
* - `cause` — the underlying failure, for the operator log only; it never reaches the caller.
|
|
10
|
+
*/
|
|
11
|
+
export declare class OAuthErrorExtras {
|
|
12
|
+
readonly errorUri?: string | undefined;
|
|
13
|
+
readonly wwwAuthenticate?: string | undefined;
|
|
14
|
+
readonly cause?: Error | undefined;
|
|
15
|
+
constructor(errorUri?: string | undefined, wwwAuthenticate?: string | undefined, cause?: Error | undefined);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* A refusal from an app-hosted OAuth endpoint (token, registration, a bearer-protected resource),
|
|
19
|
+
* answered in the RFC shape `{ error, error_description?, error_uri? }` and NOT in the webpieces
|
|
20
|
+
* `ApiError` envelope (#1176).
|
|
21
|
+
*
|
|
22
|
+
* WHY A SEPARATE TYPE: the caller of an OAuth endpoint is an OAuth CLIENT (an MCP client, Claude,
|
|
23
|
+
* any RFC 6749 library), which parses the RFC body and nothing else. And a refused grant is the
|
|
24
|
+
* protocol working as designed, so it is logged as an expected refusal, not as a server ERROR. It is
|
|
25
|
+
* deliberately not an `ApiError`: its wire shape, status rules and audience are all different.
|
|
26
|
+
*
|
|
27
|
+
* THE STATUS IS DERIVED FROM THE CODE ({@link OAuthErrorStatus}); a throw site never types one. The
|
|
28
|
+
* single exception is the single one the RFCs make: `invalid_client` may be 400 or 401 (default 401).
|
|
29
|
+
* That is enforced by the OVERLOADS, not by a runtime check — a status argument only compiles for
|
|
30
|
+
* `invalid_client`, and only 400 or 401 (`OAuthProtocolErrorCompileAssertions` pins both):
|
|
31
|
+
*
|
|
32
|
+
* ```ts
|
|
33
|
+
* throw new OAuthProtocolError(OAuthErrorCode.INVALID_GRANT, 'authorization code expired');
|
|
34
|
+
* throw new OAuthProtocolError(OAuthErrorCode.INVALID_CLIENT, 'unknown client', undefined, 400);
|
|
35
|
+
* throw new OAuthProtocolError(OAuthErrorCode.INVALID_GRANT, 'x', undefined, 401); // does not compile
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* `description` is the RFC `error_description`: SENT to the caller, and meant for the client
|
|
39
|
+
* developer — never put a secret or an end-user sentence in it. It also forms `message`, which is
|
|
40
|
+
* what the operator log shows.
|
|
41
|
+
*
|
|
42
|
+
* Rendered by `OAuthErrorResponse` — which `WebpiecesDefaultErrorTranslator` (webpieces routes),
|
|
43
|
+
* `OAuthExpressErrorHandler` (raw express routes, @webpieces/http-server) and the MCP server's bearer
|
|
44
|
+
* boundary all use, so every path answers the same bytes.
|
|
45
|
+
*/
|
|
46
|
+
export declare class OAuthProtocolError extends Error {
|
|
47
|
+
/** The RFC `error` code. */
|
|
48
|
+
readonly error: OAuthErrorCode;
|
|
49
|
+
/** The HTTP status, derived from `error` (see the class doc). */
|
|
50
|
+
readonly status: OAuthHttpStatus;
|
|
51
|
+
/** The RFC `error_description`, sent to the caller. */
|
|
52
|
+
readonly description?: string;
|
|
53
|
+
/** The RFC `error_uri`, sent to the caller. */
|
|
54
|
+
readonly errorUri?: string;
|
|
55
|
+
/** The `WWW-Authenticate` header value, when the caller must be challenged. */
|
|
56
|
+
readonly wwwAuthenticate?: string;
|
|
57
|
+
constructor(error: OAuthFixedStatusErrorCode, description?: string, extras?: OAuthErrorExtras);
|
|
58
|
+
constructor(error: OAuthErrorCode.INVALID_CLIENT, description?: string, extras?: OAuthErrorExtras, status?: InvalidClientStatus);
|
|
59
|
+
/** True for a refusal of the CALLER (every 4xx); false for `server_error` / `temporarily_unavailable`. */
|
|
60
|
+
isCallerError(): boolean;
|
|
61
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OAuthProtocolError = exports.OAuthErrorExtras = void 0;
|
|
4
|
+
const OAuthErrorCode_1 = require("./OAuthErrorCode");
|
|
5
|
+
/**
|
|
6
|
+
* The optional RFC extras of an {@link OAuthProtocolError}. Data only, so a class.
|
|
7
|
+
*
|
|
8
|
+
* - `errorUri` — RFC 6749 `error_uri`: a page a client DEVELOPER can read about the error.
|
|
9
|
+
* - `wwwAuthenticate` — the `WWW-Authenticate` value to send. Build the Bearer form with
|
|
10
|
+
* `OAuthBearerChallenge`. When it is omitted for `invalid_token` / `insufficient_scope`, the
|
|
11
|
+
* renderer derives the minimal `Bearer error="..."` challenge RFC 6750 requires.
|
|
12
|
+
* - `cause` — the underlying failure, for the operator log only; it never reaches the caller.
|
|
13
|
+
*/
|
|
14
|
+
class OAuthErrorExtras {
|
|
15
|
+
errorUri;
|
|
16
|
+
wwwAuthenticate;
|
|
17
|
+
cause;
|
|
18
|
+
constructor(errorUri, wwwAuthenticate, cause) {
|
|
19
|
+
this.errorUri = errorUri;
|
|
20
|
+
this.wwwAuthenticate = wwwAuthenticate;
|
|
21
|
+
this.cause = cause;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
exports.OAuthErrorExtras = OAuthErrorExtras;
|
|
25
|
+
/**
|
|
26
|
+
* A refusal from an app-hosted OAuth endpoint (token, registration, a bearer-protected resource),
|
|
27
|
+
* answered in the RFC shape `{ error, error_description?, error_uri? }` and NOT in the webpieces
|
|
28
|
+
* `ApiError` envelope (#1176).
|
|
29
|
+
*
|
|
30
|
+
* WHY A SEPARATE TYPE: the caller of an OAuth endpoint is an OAuth CLIENT (an MCP client, Claude,
|
|
31
|
+
* any RFC 6749 library), which parses the RFC body and nothing else. And a refused grant is the
|
|
32
|
+
* protocol working as designed, so it is logged as an expected refusal, not as a server ERROR. It is
|
|
33
|
+
* deliberately not an `ApiError`: its wire shape, status rules and audience are all different.
|
|
34
|
+
*
|
|
35
|
+
* THE STATUS IS DERIVED FROM THE CODE ({@link OAuthErrorStatus}); a throw site never types one. The
|
|
36
|
+
* single exception is the single one the RFCs make: `invalid_client` may be 400 or 401 (default 401).
|
|
37
|
+
* That is enforced by the OVERLOADS, not by a runtime check — a status argument only compiles for
|
|
38
|
+
* `invalid_client`, and only 400 or 401 (`OAuthProtocolErrorCompileAssertions` pins both):
|
|
39
|
+
*
|
|
40
|
+
* ```ts
|
|
41
|
+
* throw new OAuthProtocolError(OAuthErrorCode.INVALID_GRANT, 'authorization code expired');
|
|
42
|
+
* throw new OAuthProtocolError(OAuthErrorCode.INVALID_CLIENT, 'unknown client', undefined, 400);
|
|
43
|
+
* throw new OAuthProtocolError(OAuthErrorCode.INVALID_GRANT, 'x', undefined, 401); // does not compile
|
|
44
|
+
* ```
|
|
45
|
+
*
|
|
46
|
+
* `description` is the RFC `error_description`: SENT to the caller, and meant for the client
|
|
47
|
+
* developer — never put a secret or an end-user sentence in it. It also forms `message`, which is
|
|
48
|
+
* what the operator log shows.
|
|
49
|
+
*
|
|
50
|
+
* Rendered by `OAuthErrorResponse` — which `WebpiecesDefaultErrorTranslator` (webpieces routes),
|
|
51
|
+
* `OAuthExpressErrorHandler` (raw express routes, @webpieces/http-server) and the MCP server's bearer
|
|
52
|
+
* boundary all use, so every path answers the same bytes.
|
|
53
|
+
*/
|
|
54
|
+
class OAuthProtocolError extends Error {
|
|
55
|
+
/** The RFC `error` code. */
|
|
56
|
+
error;
|
|
57
|
+
/** The HTTP status, derived from `error` (see the class doc). */
|
|
58
|
+
status;
|
|
59
|
+
/** The RFC `error_description`, sent to the caller. */
|
|
60
|
+
description;
|
|
61
|
+
/** The RFC `error_uri`, sent to the caller. */
|
|
62
|
+
errorUri;
|
|
63
|
+
/** The `WWW-Authenticate` header value, when the caller must be challenged. */
|
|
64
|
+
wwwAuthenticate;
|
|
65
|
+
constructor(error, description, extras = new OAuthErrorExtras(), status) {
|
|
66
|
+
super(description === undefined ? `OAuth ${error}` : `OAuth ${error}: ${description}`, {
|
|
67
|
+
cause: extras.cause,
|
|
68
|
+
});
|
|
69
|
+
this.name = 'OAuthProtocolError';
|
|
70
|
+
this.error = error;
|
|
71
|
+
this.status =
|
|
72
|
+
error === OAuthErrorCode_1.OAuthErrorCode.INVALID_CLIENT ? (status ?? 401) : OAuthErrorCode_1.OAuthErrorStatus.of(error);
|
|
73
|
+
this.description = description;
|
|
74
|
+
this.errorUri = extras.errorUri;
|
|
75
|
+
this.wwwAuthenticate = extras.wwwAuthenticate;
|
|
76
|
+
}
|
|
77
|
+
/** True for a refusal of the CALLER (every 4xx); false for `server_error` / `temporarily_unavailable`. */
|
|
78
|
+
isCallerError() {
|
|
79
|
+
return this.status < 500;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
exports.OAuthProtocolError = OAuthProtocolError;
|
|
83
|
+
//# sourceMappingURL=OAuthProtocolError.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OAuthProtocolError.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/oauth/OAuthProtocolError.ts"],"names":[],"mappings":";;;AAAA,qDAM0B;AAE1B;;;;;;;;GAQG;AACH,MAAa,gBAAgB;IAEL;IACA;IACA;IAHpB,YACoB,QAAiB,EACjB,eAAwB,EACxB,KAAa;QAFb,aAAQ,GAAR,QAAQ,CAAS;QACjB,oBAAe,GAAf,eAAe,CAAS;QACxB,UAAK,GAAL,KAAK,CAAQ;IAC9B,CAAC;CACP;AAND,4CAMC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAa,kBAAmB,SAAQ,KAAK;IACzC,4BAA4B;IACnB,KAAK,CAAiB;IAC/B,iEAAiE;IACxD,MAAM,CAAkB;IACjC,uDAAuD;IAC9C,WAAW,CAAU;IAC9B,+CAA+C;IACtC,QAAQ,CAAU;IAC3B,+EAA+E;IACtE,eAAe,CAAU;IASlC,YACI,KAAqB,EACrB,WAAoB,EACpB,SAA2B,IAAI,gBAAgB,EAAE,EACjD,MAA4B;QAE5B,KAAK,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,KAAK,WAAW,EAAE,EAAE;YACnF,KAAK,EAAE,MAAM,CAAC,KAAK;SACtB,CAAC,CAAC;QACH,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;QACjC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,MAAM;YACP,KAAK,KAAK,+BAAc,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,iCAAgB,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;QAC3F,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;QAChC,IAAI,CAAC,eAAe,GAAG,MAAM,CAAC,eAAe,CAAC;IAClD,CAAC;IAED,0GAA0G;IAC1G,aAAa;QACT,OAAO,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC;IAC7B,CAAC;CACJ;AAzCD,gDAyCC","sourcesContent":["import {\n InvalidClientStatus,\n OAuthErrorCode,\n OAuthErrorStatus,\n OAuthFixedStatusErrorCode,\n OAuthHttpStatus,\n} from './OAuthErrorCode';\n\n/**\n * The optional RFC extras of an {@link OAuthProtocolError}. Data only, so a class.\n *\n * - `errorUri` — RFC 6749 `error_uri`: a page a client DEVELOPER can read about the error.\n * - `wwwAuthenticate` — the `WWW-Authenticate` value to send. Build the Bearer form with\n * `OAuthBearerChallenge`. When it is omitted for `invalid_token` / `insufficient_scope`, the\n * renderer derives the minimal `Bearer error=\"...\"` challenge RFC 6750 requires.\n * - `cause` — the underlying failure, for the operator log only; it never reaches the caller.\n */\nexport class OAuthErrorExtras {\n constructor(\n public readonly errorUri?: string,\n public readonly wwwAuthenticate?: string,\n public readonly cause?: Error,\n ) {}\n}\n\n/**\n * A refusal from an app-hosted OAuth endpoint (token, registration, a bearer-protected resource),\n * answered in the RFC shape `{ error, error_description?, error_uri? }` and NOT in the webpieces\n * `ApiError` envelope (#1176).\n *\n * WHY A SEPARATE TYPE: the caller of an OAuth endpoint is an OAuth CLIENT (an MCP client, Claude,\n * any RFC 6749 library), which parses the RFC body and nothing else. And a refused grant is the\n * protocol working as designed, so it is logged as an expected refusal, not as a server ERROR. It is\n * deliberately not an `ApiError`: its wire shape, status rules and audience are all different.\n *\n * THE STATUS IS DERIVED FROM THE CODE ({@link OAuthErrorStatus}); a throw site never types one. The\n * single exception is the single one the RFCs make: `invalid_client` may be 400 or 401 (default 401).\n * That is enforced by the OVERLOADS, not by a runtime check — a status argument only compiles for\n * `invalid_client`, and only 400 or 401 (`OAuthProtocolErrorCompileAssertions` pins both):\n *\n * ```ts\n * throw new OAuthProtocolError(OAuthErrorCode.INVALID_GRANT, 'authorization code expired');\n * throw new OAuthProtocolError(OAuthErrorCode.INVALID_CLIENT, 'unknown client', undefined, 400);\n * throw new OAuthProtocolError(OAuthErrorCode.INVALID_GRANT, 'x', undefined, 401); // does not compile\n * ```\n *\n * `description` is the RFC `error_description`: SENT to the caller, and meant for the client\n * developer — never put a secret or an end-user sentence in it. It also forms `message`, which is\n * what the operator log shows.\n *\n * Rendered by `OAuthErrorResponse` — which `WebpiecesDefaultErrorTranslator` (webpieces routes),\n * `OAuthExpressErrorHandler` (raw express routes, @webpieces/http-server) and the MCP server's bearer\n * boundary all use, so every path answers the same bytes.\n */\nexport class OAuthProtocolError extends Error {\n /** The RFC `error` code. */\n readonly error: OAuthErrorCode;\n /** The HTTP status, derived from `error` (see the class doc). */\n readonly status: OAuthHttpStatus;\n /** The RFC `error_description`, sent to the caller. */\n readonly description?: string;\n /** The RFC `error_uri`, sent to the caller. */\n readonly errorUri?: string;\n /** The `WWW-Authenticate` header value, when the caller must be challenged. */\n readonly wwwAuthenticate?: string;\n\n constructor(error: OAuthFixedStatusErrorCode, description?: string, extras?: OAuthErrorExtras);\n constructor(\n error: OAuthErrorCode.INVALID_CLIENT,\n description?: string,\n extras?: OAuthErrorExtras,\n status?: InvalidClientStatus,\n );\n constructor(\n error: OAuthErrorCode,\n description?: string,\n extras: OAuthErrorExtras = new OAuthErrorExtras(),\n status?: InvalidClientStatus,\n ) {\n super(description === undefined ? `OAuth ${error}` : `OAuth ${error}: ${description}`, {\n cause: extras.cause,\n });\n this.name = 'OAuthProtocolError';\n this.error = error;\n this.status =\n error === OAuthErrorCode.INVALID_CLIENT ? (status ?? 401) : OAuthErrorStatus.of(error);\n this.description = description;\n this.errorUri = extras.errorUri;\n this.wwwAuthenticate = extras.wwwAuthenticate;\n }\n\n /** True for a refusal of the CALLER (every 4xx); false for `server_error` / `temporarily_unavailable`. */\n isCallerError(): boolean {\n return this.status < 500;\n }\n}\n"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* COMPILE-TIME assertions for {@link OAuthProtocolError}'s status rule: only `invalid_client` takes
|
|
3
|
+
* a status, and only 400 or 401. Each `@ts-expect-error` fails the build (TS2578) if its line ever
|
|
4
|
+
* starts compiling. A non-spec file on purpose: vitest strips types and tsconfig.lib.json excludes
|
|
5
|
+
* specs, so only a compiled file makes these assertions real.
|
|
6
|
+
*/
|
|
7
|
+
export declare class OAuthProtocolErrorCompileAssertions {
|
|
8
|
+
legitimate(): void;
|
|
9
|
+
illegal(): void;
|
|
10
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OAuthProtocolErrorCompileAssertions = void 0;
|
|
4
|
+
const OAuthAuthorizationErrorRedirect_1 = require("./OAuthAuthorizationErrorRedirect");
|
|
5
|
+
const OAuthBearerChallenge_1 = require("./OAuthBearerChallenge");
|
|
6
|
+
const OAuthErrorCode_1 = require("./OAuthErrorCode");
|
|
7
|
+
const OAuthProtocolError_1 = require("./OAuthProtocolError");
|
|
8
|
+
/**
|
|
9
|
+
* COMPILE-TIME assertions for {@link OAuthProtocolError}'s status rule: only `invalid_client` takes
|
|
10
|
+
* a status, and only 400 or 401. Each `@ts-expect-error` fails the build (TS2578) if its line ever
|
|
11
|
+
* starts compiling. A non-spec file on purpose: vitest strips types and tsconfig.lib.json excludes
|
|
12
|
+
* specs, so only a compiled file makes these assertions real.
|
|
13
|
+
*/
|
|
14
|
+
class OAuthProtocolErrorCompileAssertions {
|
|
15
|
+
legitimate() {
|
|
16
|
+
void new OAuthProtocolError_1.OAuthProtocolError(OAuthErrorCode_1.OAuthErrorCode.INVALID_GRANT);
|
|
17
|
+
void new OAuthProtocolError_1.OAuthProtocolError(OAuthErrorCode_1.OAuthErrorCode.INVALID_GRANT, 'code expired');
|
|
18
|
+
void new OAuthProtocolError_1.OAuthProtocolError(OAuthErrorCode_1.OAuthErrorCode.INVALID_TOKEN, 'expired', new OAuthProtocolError_1.OAuthErrorExtras());
|
|
19
|
+
void new OAuthProtocolError_1.OAuthProtocolError(OAuthErrorCode_1.OAuthErrorCode.INVALID_CLIENT, 'unknown client');
|
|
20
|
+
void new OAuthProtocolError_1.OAuthProtocolError(OAuthErrorCode_1.OAuthErrorCode.INVALID_CLIENT, 'x', undefined, 400);
|
|
21
|
+
void new OAuthProtocolError_1.OAuthProtocolError(OAuthErrorCode_1.OAuthErrorCode.INVALID_CLIENT, 'x', undefined, 401);
|
|
22
|
+
void new OAuthBearerChallenge_1.OAuthBearerChallenge(OAuthErrorCode_1.OAuthErrorCode.INSUFFICIENT_SCOPE, 'x', 'tools:write');
|
|
23
|
+
void new OAuthAuthorizationErrorRedirect_1.OAuthAuthorizationErrorRedirect('https://c/cb', OAuthErrorCode_1.OAuthErrorCode.ACCESS_DENIED, 's');
|
|
24
|
+
}
|
|
25
|
+
illegal() {
|
|
26
|
+
// @ts-expect-error -- the RFCs fix invalid_grant's status; a status argument does not compile
|
|
27
|
+
void new OAuthProtocolError_1.OAuthProtocolError(OAuthErrorCode_1.OAuthErrorCode.INVALID_GRANT, 'x', undefined, 401);
|
|
28
|
+
// @ts-expect-error -- invalid_client may only be 400 or 401
|
|
29
|
+
void new OAuthProtocolError_1.OAuthProtocolError(OAuthErrorCode_1.OAuthErrorCode.INVALID_CLIENT, 'x', undefined, 403);
|
|
30
|
+
// @ts-expect-error -- a code outside the RFC vocabulary does not compile
|
|
31
|
+
void new OAuthProtocolError_1.OAuthProtocolError('invalid_credential', 'x');
|
|
32
|
+
// @ts-expect-error -- invalid_grant is a token-endpoint code, not a Bearer challenge code
|
|
33
|
+
void new OAuthBearerChallenge_1.OAuthBearerChallenge(OAuthErrorCode_1.OAuthErrorCode.INVALID_GRANT);
|
|
34
|
+
// @ts-expect-error -- invalid_token is not an authorization-endpoint redirect code
|
|
35
|
+
void new OAuthAuthorizationErrorRedirect_1.OAuthAuthorizationErrorRedirect('https://c/cb', OAuthErrorCode_1.OAuthErrorCode.INVALID_TOKEN, 's');
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
exports.OAuthProtocolErrorCompileAssertions = OAuthProtocolErrorCompileAssertions;
|
|
39
|
+
//# sourceMappingURL=OAuthProtocolErrorCompileAssertions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OAuthProtocolErrorCompileAssertions.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/oauth/OAuthProtocolErrorCompileAssertions.ts"],"names":[],"mappings":";;;AAAA,uFAAoF;AACpF,iEAA8D;AAC9D,qDAAkD;AAClD,6DAA4E;AAE5E;;;;;GAKG;AACH,MAAa,mCAAmC;IAC5C,UAAU;QACN,KAAK,IAAI,uCAAkB,CAAC,+BAAc,CAAC,aAAa,CAAC,CAAC;QAC1D,KAAK,IAAI,uCAAkB,CAAC,+BAAc,CAAC,aAAa,EAAE,cAAc,CAAC,CAAC;QAC1E,KAAK,IAAI,uCAAkB,CACvB,+BAAc,CAAC,aAAa,EAC5B,SAAS,EACT,IAAI,qCAAgB,EAAE,CACzB,CAAC;QACF,KAAK,IAAI,uCAAkB,CAAC,+BAAc,CAAC,cAAc,EAAE,gBAAgB,CAAC,CAAC;QAC7E,KAAK,IAAI,uCAAkB,CAAC,+BAAc,CAAC,cAAc,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,CAAC,CAAC;QAChF,KAAK,IAAI,uCAAkB,CAAC,+BAAc,CAAC,cAAc,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,CAAC,CAAC;QAChF,KAAK,IAAI,2CAAoB,CAAC,+BAAc,CAAC,kBAAkB,EAAE,GAAG,EAAE,aAAa,CAAC,CAAC;QACrF,KAAK,IAAI,iEAA+B,CAAC,cAAc,EAAE,+BAAc,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;IAChG,CAAC;IAED,OAAO;QACH,8FAA8F;QAC9F,KAAK,IAAI,uCAAkB,CAAC,+BAAc,CAAC,aAAa,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,CAAC,CAAC;QAC/E,4DAA4D;QAC5D,KAAK,IAAI,uCAAkB,CAAC,+BAAc,CAAC,cAAc,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,CAAC,CAAC;QAChF,yEAAyE;QACzE,KAAK,IAAI,uCAAkB,CAAC,oBAAoB,EAAE,GAAG,CAAC,CAAC;QACvD,0FAA0F;QAC1F,KAAK,IAAI,2CAAoB,CAAC,+BAAc,CAAC,aAAa,CAAC,CAAC;QAC5D,mFAAmF;QACnF,KAAK,IAAI,iEAA+B,CAAC,cAAc,EAAE,+BAAc,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;IAChG,CAAC;CACJ;AA5BD,kFA4BC","sourcesContent":["import { OAuthAuthorizationErrorRedirect } from './OAuthAuthorizationErrorRedirect';\nimport { OAuthBearerChallenge } from './OAuthBearerChallenge';\nimport { OAuthErrorCode } from './OAuthErrorCode';\nimport { OAuthErrorExtras, OAuthProtocolError } from './OAuthProtocolError';\n\n/**\n * COMPILE-TIME assertions for {@link OAuthProtocolError}'s status rule: only `invalid_client` takes\n * a status, and only 400 or 401. Each `@ts-expect-error` fails the build (TS2578) if its line ever\n * starts compiling. A non-spec file on purpose: vitest strips types and tsconfig.lib.json excludes\n * specs, so only a compiled file makes these assertions real.\n */\nexport class OAuthProtocolErrorCompileAssertions {\n legitimate(): void {\n void new OAuthProtocolError(OAuthErrorCode.INVALID_GRANT);\n void new OAuthProtocolError(OAuthErrorCode.INVALID_GRANT, 'code expired');\n void new OAuthProtocolError(\n OAuthErrorCode.INVALID_TOKEN,\n 'expired',\n new OAuthErrorExtras(),\n );\n void new OAuthProtocolError(OAuthErrorCode.INVALID_CLIENT, 'unknown client');\n void new OAuthProtocolError(OAuthErrorCode.INVALID_CLIENT, 'x', undefined, 400);\n void new OAuthProtocolError(OAuthErrorCode.INVALID_CLIENT, 'x', undefined, 401);\n void new OAuthBearerChallenge(OAuthErrorCode.INSUFFICIENT_SCOPE, 'x', 'tools:write');\n void new OAuthAuthorizationErrorRedirect('https://c/cb', OAuthErrorCode.ACCESS_DENIED, 's');\n }\n\n illegal(): void {\n // @ts-expect-error -- the RFCs fix invalid_grant's status; a status argument does not compile\n void new OAuthProtocolError(OAuthErrorCode.INVALID_GRANT, 'x', undefined, 401);\n // @ts-expect-error -- invalid_client may only be 400 or 401\n void new OAuthProtocolError(OAuthErrorCode.INVALID_CLIENT, 'x', undefined, 403);\n // @ts-expect-error -- a code outside the RFC vocabulary does not compile\n void new OAuthProtocolError('invalid_credential', 'x');\n // @ts-expect-error -- invalid_grant is a token-endpoint code, not a Bearer challenge code\n void new OAuthBearerChallenge(OAuthErrorCode.INVALID_GRANT);\n // @ts-expect-error -- invalid_token is not an authorization-endpoint redirect code\n void new OAuthAuthorizationErrorRedirect('https://c/cb', OAuthErrorCode.INVALID_TOKEN, 's');\n }\n}\n"]}
|