@webpieces/core-util 0.4.863 → 0.4.865

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.
Files changed (55) hide show
  1. package/package.json +1 -1
  2. package/src/ContextKey.d.ts +3 -3
  3. package/src/ContextKey.js +3 -3
  4. package/src/ContextKey.js.map +1 -1
  5. package/src/http/ContextMgr.d.ts +3 -3
  6. package/src/http/ContextMgr.js +3 -3
  7. package/src/http/ContextMgr.js.map +1 -1
  8. package/src/http/DeletedAuthCompileAssertions.d.ts +4 -0
  9. package/src/http/DeletedAuthCompileAssertions.js +30 -0
  10. package/src/http/DeletedAuthCompileAssertions.js.map +1 -0
  11. package/src/http/DestinationTrust.d.ts +6 -6
  12. package/src/http/DestinationTrust.js +12 -13
  13. package/src/http/DestinationTrust.js.map +1 -1
  14. package/src/http/HttpEndpointOptions.d.ts +2 -2
  15. package/src/http/HttpEndpointOptions.js.map +1 -1
  16. package/src/http/RouteMetadata.d.ts +6 -1
  17. package/src/http/RouteMetadata.js +14 -1
  18. package/src/http/RouteMetadata.js.map +1 -1
  19. package/src/http/RouteMetadataFactory.js +9 -3
  20. package/src/http/RouteMetadataFactory.js.map +1 -1
  21. package/src/http/RuntimeLocality.d.ts +3 -3
  22. package/src/http/RuntimeLocality.js +1 -1
  23. package/src/http/RuntimeLocality.js.map +1 -1
  24. package/src/http/Secrets.d.ts +2 -2
  25. package/src/http/Secrets.js +2 -2
  26. package/src/http/Secrets.js.map +1 -1
  27. package/src/http/Surface.d.ts +4 -4
  28. package/src/http/Surface.js.map +1 -1
  29. package/src/http/WebpiecesCoreHeaders.d.ts +3 -3
  30. package/src/http/WebpiecesCoreHeaders.js +3 -3
  31. package/src/http/WebpiecesCoreHeaders.js.map +1 -1
  32. package/src/http/WpAuthApiKeyCompileAssertions.d.ts +4 -8
  33. package/src/http/WpAuthApiKeyCompileAssertions.js +8 -13
  34. package/src/http/WpAuthApiKeyCompileAssertions.js.map +1 -1
  35. package/src/http/WpAuthJwtCompileAssertions.d.ts +1 -27
  36. package/src/http/WpAuthJwtCompileAssertions.js +13 -48
  37. package/src/http/WpAuthJwtCompileAssertions.js.map +1 -1
  38. package/src/http/WpAuthorizationCompileAssertions.d.ts +1 -0
  39. package/src/http/WpAuthorizationCompileAssertions.js +26 -0
  40. package/src/http/WpAuthorizationCompileAssertions.js.map +1 -0
  41. package/src/http/auth-mode.d.ts +22 -130
  42. package/src/http/auth-mode.js +100 -26
  43. package/src/http/auth-mode.js.map +1 -1
  44. package/src/http/authorization.d.ts +38 -0
  45. package/src/http/authorization.js +71 -0
  46. package/src/http/authorization.js.map +1 -0
  47. package/src/http/decorators.d.ts +11 -114
  48. package/src/http/decorators.js +47 -157
  49. package/src/http/decorators.js.map +1 -1
  50. package/src/index.d.ts +5 -4
  51. package/src/index.js +21 -19
  52. package/src/index.js.map +1 -1
  53. package/src/mcp/McpAuthMetadata.d.ts +0 -9
  54. package/src/mcp/McpAuthMetadata.js +0 -31
  55. package/src/mcp/McpAuthMetadata.js.map +0 -1
@@ -1,31 +1,5 @@
1
- /**
2
- * COMPILE-TIME assertions for `JwtRoles` — the guarantee that every broken role decision is a compile
3
- * error rather than a silent over-permissioning. Each `@ts-expect-error` below FAILS THE BUILD (TS2578,
4
- * "unused '@ts-expect-error' directive") if the line it guards ever starts compiling. Verified by
5
- * deliberately making a guarded line valid and watching `tsc -p tsconfig.lib.json` go red.
6
- *
7
- * WHY THIS IS NOT A `.spec.ts` FILE. It was, and it proved nothing: tsconfig.lib.json EXCLUDES spec
8
- * files, and vitest transpiles with esbuild, which strips types without checking them. So a
9
- * `@ts-expect-error` in a spec is inert — the suite passes whether or not the guarded line really
10
- * errors. Verified the hard way: with one guarded line deliberately made VALID, both `vitest run` and
11
- * `nx run core-util:ci` stayed green. A type-level guarantee must be asserted in a file the
12
- * type-checker actually compiles, which is any non-spec file under `src/`.
13
- *
14
- * WHY IT CALLS THE FACTORIES INSTEAD OF USING DECORATOR SYNTAX. A decorator factory is a plain
15
- * function, so a direct call type-checks the argument identically — and the first cut of this file,
16
- * which used `@ApiPath` + `@Endpoint` on an abstract class, made the architecture scanner classify
17
- * core-util as exporting an API contract and demand a `role:api-lib` tag. A test fixture must not
18
- * change what the repo believes about its own architecture.
19
- *
20
- * The counterpart RUNTIME behaviour (that `allRolesAllowed: true` reads back as `[]` through
21
- * `rolesRequired`) is asserted in `__tests__/pubsub-and-auth-decorators.spec.ts`, where runtime
22
- * assertions belong.
23
- */
1
+ /** Type assertions run in the library build because vitest erases types. */
24
2
  export declare class AuthJwtCompileAssertions {
25
- /** The two legitimate spellings must keep compiling; this half is asserted by the ABSENCE of error. */
26
3
  legitimate(): void;
27
- /** Every one of these must be UNWRITABLE. A directive going unused here fails the build. */
28
4
  rejected(): void;
29
- /** Class-level auth readers were deleted; every metadata lookup must name the audited method. */
30
- readersRequireMethodName(): void;
31
5
  }
@@ -2,59 +2,24 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.AuthJwtCompileAssertions = void 0;
4
4
  const decorators_1 = require("./decorators");
5
- /**
6
- * COMPILE-TIME assertions for `JwtRoles` — the guarantee that every broken role decision is a compile
7
- * error rather than a silent over-permissioning. Each `@ts-expect-error` below FAILS THE BUILD (TS2578,
8
- * "unused '@ts-expect-error' directive") if the line it guards ever starts compiling. Verified by
9
- * deliberately making a guarded line valid and watching `tsc -p tsconfig.lib.json` go red.
10
- *
11
- * WHY THIS IS NOT A `.spec.ts` FILE. It was, and it proved nothing: tsconfig.lib.json EXCLUDES spec
12
- * files, and vitest transpiles with esbuild, which strips types without checking them. So a
13
- * `@ts-expect-error` in a spec is inert — the suite passes whether or not the guarded line really
14
- * errors. Verified the hard way: with one guarded line deliberately made VALID, both `vitest run` and
15
- * `nx run core-util:ci` stayed green. A type-level guarantee must be asserted in a file the
16
- * type-checker actually compiles, which is any non-spec file under `src/`.
17
- *
18
- * WHY IT CALLS THE FACTORIES INSTEAD OF USING DECORATOR SYNTAX. A decorator factory is a plain
19
- * function, so a direct call type-checks the argument identically — and the first cut of this file,
20
- * which used `@ApiPath` + `@Endpoint` on an abstract class, made the architecture scanner classify
21
- * core-util as exporting an API contract and demand a `role:api-lib` tag. A test fixture must not
22
- * change what the repo believes about its own architecture.
23
- *
24
- * The counterpart RUNTIME behaviour (that `allRolesAllowed: true` reads back as `[]` through
25
- * `rolesRequired`) is asserted in `__tests__/pubsub-and-auth-decorators.spec.ts`, where runtime
26
- * assertions belong.
27
- */
5
+ const auth_mode_1 = require("./auth-mode");
6
+ // @ts-expect-error standalone JWT declarations are removed; use WpAuth([jwt()]) plus WpAuthorization
7
+ const decorators_2 = require("./decorators");
8
+ /** Type assertions run in the library build because vitest erases types. */
28
9
  class AuthJwtCompileAssertions {
29
- /** The two legitimate spellings must keep compiling; this half is asserted by the ABSENCE of error. */
30
10
  legitimate() {
31
- void (0, decorators_1.WpAuthJwt)({ roles: ['admin'] });
32
- void (0, decorators_1.WpAuthJwt)({ roles: ['admin', 'editor'] });
33
- void (0, decorators_1.WpAuthJwt)({ allRolesAllowed: true });
34
- void (0, decorators_1.WpAuthJwt)({ allRolesAllowed: true, inOrg: true });
35
- void (0, decorators_1.WpAuthJwt)({ roles: ['admin'], tenantScoped: true });
11
+ void (0, decorators_1.WpAuth)([(0, auth_mode_1.jwt)()]);
36
12
  }
37
- /** Every one of these must be UNWRITABLE. A directive going unused here fails the build. */
38
13
  rejected() {
39
- // @ts-expect-error pick a branch — {} may not mean "any authenticated user"
40
- void (0, decorators_1.WpAuthJwt)({});
41
- // @ts-expect-error roles needs AT LEAST ONE entry; [] was the old silent widest grant
42
- void (0, decorators_1.WpAuthJwt)({ roles: [] });
43
- // @ts-expect-error contradictory: wide AND role-gated at the same time
44
- void (0, decorators_1.WpAuthJwt)({ allRolesAllowed: true, roles: ['admin'] });
45
- // @ts-expect-error allRolesAllowed:false is a redundant SECOND spelling of role-gating
46
- void (0, decorators_1.WpAuthJwt)({ roles: ['admin'], allRolesAllowed: false });
47
- // @ts-expect-error `false` is never how you say "wide" — the other branch is for that
48
- void (0, decorators_1.WpAuthJwt)({ allRolesAllowed: false });
49
- // @ts-expect-error app-defined fields alone authorize nothing; a role decision is mandatory
50
- void (0, decorators_1.WpAuthJwt)({ inOrg: true });
51
- }
52
- /** Class-level auth readers were deleted; every metadata lookup must name the audited method. */
53
- readersRequireMethodName() {
54
- // @ts-expect-error methodName is mandatory so removed class-level auth cannot silently compile
14
+ // @ts-expect-error a protected declaration requires at least one credential descriptor
15
+ void (0, decorators_1.WpAuth)([]);
16
+ // @ts-expect-error policy belongs on WpAuthorization
17
+ void (0, auth_mode_1.jwt)({ roles: ['admin'] });
18
+ // @ts-expect-error method metadata cannot be read without the method name
55
19
  void (0, decorators_1.getAuthMeta)(AuthJwtCompileAssertions);
56
- // @ts-expect-error methodName is mandatory so removed class-level auth cannot silently compile
57
- void (0, decorators_1.getAuthMode)(AuthJwtCompileAssertions);
20
+ void decorators_2.WpAuthJwt;
21
+ void decorators_2.getAuthMode;
22
+ void decorators_2.rolesRequired;
58
23
  }
59
24
  }
60
25
  exports.AuthJwtCompileAssertions = AuthJwtCompileAssertions;
@@ -1 +1 @@
1
- {"version":3,"file":"WpAuthJwtCompileAssertions.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/WpAuthJwtCompileAssertions.ts"],"names":[],"mappings":";;;AAAA,6CAAmE;AAEnE;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAa,wBAAwB;IACjC,uGAAuG;IACvG,UAAU;QACN,KAAK,IAAA,sBAAS,EAAC,EAAE,KAAK,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACrC,KAAK,IAAA,sBAAS,EAAC,EAAE,KAAK,EAAE,CAAC,OAAO,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC;QAC/C,KAAK,IAAA,sBAAS,EAAC,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1C,KAAK,IAAA,sBAAS,EAAC,EAAE,eAAe,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACvD,KAAK,IAAA,sBAAS,EAAC,EAAE,KAAK,EAAE,CAAC,OAAO,CAAC,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7D,CAAC;IAED,4FAA4F;IAC5F,QAAQ;QACJ,4EAA4E;QAC5E,KAAK,IAAA,sBAAS,EAAC,EAAE,CAAC,CAAC;QACnB,sFAAsF;QACtF,KAAK,IAAA,sBAAS,EAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;QAC9B,uEAAuE;QACvE,KAAK,IAAA,sBAAS,EAAC,EAAE,eAAe,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC5D,uFAAuF;QACvF,KAAK,IAAA,sBAAS,EAAC,EAAE,KAAK,EAAE,CAAC,OAAO,CAAC,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC,CAAC;QAC7D,sFAAsF;QACtF,KAAK,IAAA,sBAAS,EAAC,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC,CAAC;QAC3C,4FAA4F;QAC5F,KAAK,IAAA,sBAAS,EAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACpC,CAAC;IAED,iGAAiG;IACjG,wBAAwB;QACpB,+FAA+F;QAC/F,KAAK,IAAA,wBAAW,EAAC,wBAAwB,CAAC,CAAC;QAC3C,+FAA+F;QAC/F,KAAK,IAAA,wBAAW,EAAC,wBAAwB,CAAC,CAAC;IAC/C,CAAC;CACJ;AAjCD,4DAiCC","sourcesContent":["import { getAuthMeta, getAuthMode, WpAuthJwt } from './decorators';\n\n/**\n * COMPILE-TIME assertions for `JwtRoles` — the guarantee that every broken role decision is a compile\n * error rather than a silent over-permissioning. Each `@ts-expect-error` below FAILS THE BUILD (TS2578,\n * \"unused '@ts-expect-error' directive\") if the line it guards ever starts compiling. Verified by\n * deliberately making a guarded line valid and watching `tsc -p tsconfig.lib.json` go red.\n *\n * WHY THIS IS NOT A `.spec.ts` FILE. It was, and it proved nothing: tsconfig.lib.json EXCLUDES spec\n * files, and vitest transpiles with esbuild, which strips types without checking them. So a\n * `@ts-expect-error` in a spec is inert — the suite passes whether or not the guarded line really\n * errors. Verified the hard way: with one guarded line deliberately made VALID, both `vitest run` and\n * `nx run core-util:ci` stayed green. A type-level guarantee must be asserted in a file the\n * type-checker actually compiles, which is any non-spec file under `src/`.\n *\n * WHY IT CALLS THE FACTORIES INSTEAD OF USING DECORATOR SYNTAX. A decorator factory is a plain\n * function, so a direct call type-checks the argument identically — and the first cut of this file,\n * which used `@ApiPath` + `@Endpoint` on an abstract class, made the architecture scanner classify\n * core-util as exporting an API contract and demand a `role:api-lib` tag. A test fixture must not\n * change what the repo believes about its own architecture.\n *\n * The counterpart RUNTIME behaviour (that `allRolesAllowed: true` reads back as `[]` through\n * `rolesRequired`) is asserted in `__tests__/pubsub-and-auth-decorators.spec.ts`, where runtime\n * assertions belong.\n */\nexport class AuthJwtCompileAssertions {\n /** The two legitimate spellings must keep compiling; this half is asserted by the ABSENCE of error. */\n legitimate(): void {\n void WpAuthJwt({ roles: ['admin'] });\n void WpAuthJwt({ roles: ['admin', 'editor'] });\n void WpAuthJwt({ allRolesAllowed: true });\n void WpAuthJwt({ allRolesAllowed: true, inOrg: true });\n void WpAuthJwt({ roles: ['admin'], tenantScoped: true });\n }\n\n /** Every one of these must be UNWRITABLE. A directive going unused here fails the build. */\n rejected(): void {\n // @ts-expect-error pick a branch — {} may not mean \"any authenticated user\"\n void WpAuthJwt({});\n // @ts-expect-error roles needs AT LEAST ONE entry; [] was the old silent widest grant\n void WpAuthJwt({ roles: [] });\n // @ts-expect-error contradictory: wide AND role-gated at the same time\n void WpAuthJwt({ allRolesAllowed: true, roles: ['admin'] });\n // @ts-expect-error allRolesAllowed:false is a redundant SECOND spelling of role-gating\n void WpAuthJwt({ roles: ['admin'], allRolesAllowed: false });\n // @ts-expect-error `false` is never how you say \"wide\" — the other branch is for that\n void WpAuthJwt({ allRolesAllowed: false });\n // @ts-expect-error app-defined fields alone authorize nothing; a role decision is mandatory\n void WpAuthJwt({ inOrg: true });\n }\n\n /** Class-level auth readers were deleted; every metadata lookup must name the audited method. */\n readersRequireMethodName(): void {\n // @ts-expect-error methodName is mandatory so removed class-level auth cannot silently compile\n void getAuthMeta(AuthJwtCompileAssertions);\n // @ts-expect-error methodName is mandatory so removed class-level auth cannot silently compile\n void getAuthMode(AuthJwtCompileAssertions);\n }\n}\n"]}
1
+ {"version":3,"file":"WpAuthJwtCompileAssertions.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/WpAuthJwtCompileAssertions.ts"],"names":[],"mappings":";;;AAAA,6CAAmD;AACnD,2CAAkC;AAClC,qGAAqG;AACrG,6CAAqE;AAErE,4EAA4E;AAC5E,MAAa,wBAAwB;IACjC,UAAU;QACN,KAAK,IAAA,mBAAM,EAAC,CAAC,IAAA,eAAG,GAAE,CAAC,CAAC,CAAC;IACzB,CAAC;IAED,QAAQ;QACJ,uFAAuF;QACvF,KAAK,IAAA,mBAAM,EAAC,EAAE,CAAC,CAAC;QAChB,qDAAqD;QACrD,KAAK,IAAA,eAAG,EAAC,EAAE,KAAK,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC/B,0EAA0E;QAC1E,KAAK,IAAA,wBAAW,EAAC,wBAAwB,CAAC,CAAC;QAC3C,KAAK,sBAAS,CAAC;QACf,KAAK,wBAAW,CAAC;QACjB,KAAK,0BAAa,CAAC;IACvB,CAAC;CACJ;AAhBD,4DAgBC","sourcesContent":["import { getAuthMeta, WpAuth } from './decorators';\nimport { jwt } from './auth-mode';\n// @ts-expect-error standalone JWT declarations are removed; use WpAuth([jwt()]) plus WpAuthorization\nimport { WpAuthJwt, getAuthMode, rolesRequired } from './decorators';\n\n/** Type assertions run in the library build because vitest erases types. */\nexport class AuthJwtCompileAssertions {\n legitimate(): void {\n void WpAuth([jwt()]);\n }\n\n rejected(): void {\n // @ts-expect-error a protected declaration requires at least one credential descriptor\n void WpAuth([]);\n // @ts-expect-error policy belongs on WpAuthorization\n void jwt({ roles: ['admin'] });\n // @ts-expect-error method metadata cannot be read without the method name\n void getAuthMeta(AuthJwtCompileAssertions);\n void WpAuthJwt;\n void getAuthMode;\n void rolesRequired;\n }\n}\n"]}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const authorization_1 = require("./authorization");
4
+ class OrderPolicy {
5
+ permission;
6
+ }
7
+ (0, authorization_1.WpAuthorization)({ authType: authorization_1.AuthorizationType.ALL_USERS });
8
+ (0, authorization_1.WpAuthorization)({ authType: authorization_1.AuthorizationType.ROLES, roles: ['editor'] });
9
+ (0, authorization_1.WpAuthorization)({ authType: authorization_1.AuthorizationType.CUSTOM, appPolicy: { permission: 'orders:edit' } });
10
+ // @ts-expect-error ROLES requires a nonempty tuple
11
+ (0, authorization_1.WpAuthorization)({ authType: authorization_1.AuthorizationType.ROLES, roles: [] });
12
+ // @ts-expect-error ROLES requires explicit roles
13
+ (0, authorization_1.WpAuthorization)({ authType: authorization_1.AuthorizationType.ROLES });
14
+ // @ts-expect-error CUSTOM requires typed application policy
15
+ (0, authorization_1.WpAuthorization)({ authType: authorization_1.AuthorizationType.CUSTOM });
16
+ // @ts-expect-error standard policies reject application policy
17
+ (0, authorization_1.WpAuthorization)({ authType: authorization_1.AuthorizationType.ALL_USERS, appPolicy: { permission: 'orders:edit' } });
18
+ // @ts-expect-error roles are accepted only by ROLES
19
+ (0, authorization_1.WpAuthorization)({ authType: authorization_1.AuthorizationType.SERVICE_ONLY, roles: ['editor'] });
20
+ // @ts-expect-error arbitrary top-level permissions belong in CUSTOM.appPolicy
21
+ (0, authorization_1.WpAuthorization)({ authType: authorization_1.AuthorizationType.ALL_USERS, permission: 'orders:read' });
22
+ // @ts-expect-error the application payload is checked by its declared type
23
+ (0, authorization_1.WpAuthorization)({ authType: authorization_1.AuthorizationType.CUSTOM, appPolicy: { permission: 'users:edit' } });
24
+ // @ts-expect-error anonymous access requires a reason
25
+ (0, authorization_1.WpAuthorization)({ authType: authorization_1.AuthorizationType.ANONYMOUS });
26
+ //# sourceMappingURL=WpAuthorizationCompileAssertions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WpAuthorizationCompileAssertions.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/WpAuthorizationCompileAssertions.ts"],"names":[],"mappings":";;AAAA,mDAAqE;AAErE,MAAM,WAAW;IACb,UAAU,CAAiC;CAC9C;AAED,IAAA,+BAAe,EAAC,EAAE,QAAQ,EAAE,iCAAiB,CAAC,SAAS,EAAE,CAAC,CAAC;AAC3D,IAAA,+BAAe,EAAC,EAAE,QAAQ,EAAE,iCAAiB,CAAC,KAAK,EAAE,KAAK,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;AAC1E,IAAA,+BAAe,EAAc,EAAE,QAAQ,EAAE,iCAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,UAAU,EAAE,aAAa,EAAE,EAAE,CAAC,CAAC;AAC/G,mDAAmD;AACnD,IAAA,+BAAe,EAAC,EAAE,QAAQ,EAAE,iCAAiB,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;AAClE,iDAAiD;AACjD,IAAA,+BAAe,EAAC,EAAE,QAAQ,EAAE,iCAAiB,CAAC,KAAK,EAAE,CAAC,CAAC;AACvD,4DAA4D;AAC5D,IAAA,+BAAe,EAAc,EAAE,QAAQ,EAAE,iCAAiB,CAAC,MAAM,EAAE,CAAC,CAAC;AACrE,+DAA+D;AAC/D,IAAA,+BAAe,EAAc,EAAE,QAAQ,EAAE,iCAAiB,CAAC,SAAS,EAAE,SAAS,EAAE,EAAE,UAAU,EAAE,aAAa,EAAE,EAAE,CAAC,CAAC;AAClH,oDAAoD;AACpD,IAAA,+BAAe,EAAC,EAAE,QAAQ,EAAE,iCAAiB,CAAC,YAAY,EAAE,KAAK,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;AACjF,8EAA8E;AAC9E,IAAA,+BAAe,EAAC,EAAE,QAAQ,EAAE,iCAAiB,CAAC,SAAS,EAAE,UAAU,EAAE,aAAa,EAAE,CAAC,CAAC;AACtF,2EAA2E;AAC3E,IAAA,+BAAe,EAAc,EAAE,QAAQ,EAAE,iCAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,UAAU,EAAE,YAAY,EAAE,EAAE,CAAC,CAAC;AAC9G,sDAAsD;AACtD,IAAA,+BAAe,EAAC,EAAE,QAAQ,EAAE,iCAAiB,CAAC,SAAS,EAAE,CAAC,CAAC","sourcesContent":["import { AuthorizationType, WpAuthorization } from './authorization';\n\nclass OrderPolicy {\n permission!: 'orders:read' | 'orders:edit';\n}\n\nWpAuthorization({ authType: AuthorizationType.ALL_USERS });\nWpAuthorization({ authType: AuthorizationType.ROLES, roles: ['editor'] });\nWpAuthorization<OrderPolicy>({ authType: AuthorizationType.CUSTOM, appPolicy: { permission: 'orders:edit' } });\n// @ts-expect-error ROLES requires a nonempty tuple\nWpAuthorization({ authType: AuthorizationType.ROLES, roles: [] });\n// @ts-expect-error ROLES requires explicit roles\nWpAuthorization({ authType: AuthorizationType.ROLES });\n// @ts-expect-error CUSTOM requires typed application policy\nWpAuthorization<OrderPolicy>({ authType: AuthorizationType.CUSTOM });\n// @ts-expect-error standard policies reject application policy\nWpAuthorization<OrderPolicy>({ authType: AuthorizationType.ALL_USERS, appPolicy: { permission: 'orders:edit' } });\n// @ts-expect-error roles are accepted only by ROLES\nWpAuthorization({ authType: AuthorizationType.SERVICE_ONLY, roles: ['editor'] });\n// @ts-expect-error arbitrary top-level permissions belong in CUSTOM.appPolicy\nWpAuthorization({ authType: AuthorizationType.ALL_USERS, permission: 'orders:read' });\n// @ts-expect-error the application payload is checked by its declared type\nWpAuthorization<OrderPolicy>({ authType: AuthorizationType.CUSTOM, appPolicy: { permission: 'users:edit' } });\n// @ts-expect-error anonymous access requires a reason\nWpAuthorization({ authType: AuthorizationType.ANONYMOUS });\n"]}
@@ -1,79 +1,3 @@
1
- /**
2
- * The TYPE layer of the auth surface: what an endpoint's credential posture IS, with no decorator and
3
- * no reflect-metadata. `decorators.ts` (which attaches these) imports FROM here, never the other way,
4
- * so a reader that only needs to switch on a mode — `DestinationTrust`, `RouteMetadata`, a spec
5
- * generator — does not drag the whole decorator surface in with it.
6
- *
7
- * Split out of `decorators.ts` purely for file size, exactly as `api-kind.ts`, `external-caller.ts` and
8
- * `RouteMetadata.ts` were before it. Nothing about these types' role changed in the move, and the
9
- * barrel keeps the package surface identical.
10
- */
11
- /**
12
- * The role decision for a JWT endpoint, as a union the COMPILER enforces — one spelling per decision,
13
- * every broken combination a compile error:
14
- *
15
- * ```typescript
16
- * @WpAuthJwt({ roles: ['admin'] }) // ✅ role-gated (any-of)
17
- * @WpAuthJwt({ allRolesAllowed: true }) // ✅ every authenticated user, said out loud
18
- * @WpAuthJwt({}) // ❌ pick a branch
19
- * @WpAuthJwt({ roles: [] }) // ❌ needs at least one role
20
- * ```
21
- *
22
- * `allRolesAllowed` exists ONLY on the wide branch (the dangerous half must be a greppable token, and
23
- * the narrow branch rejects it as a redundant second spelling); `roles` is a NON-EMPTY tuple so
24
- * "declared roles, passed none" — the old optional `string[]`'s silent widest grant — cannot be written.
25
- * All six bad cases are pinned in `AuthJwtCompileAssertions.ts` — a COMPILED file, not a spec: tsc
26
- * fails the build (TS2578) if any starts compiling. A spec cannot do this (see that file's header).
27
- *
28
- * WHY a type rather than the runtime `throw` this replaced: `.claude/review/backwards-compatibility.md`
29
- * shim shapes #4 and #5. Not restated here — three copies of one rationale is three things to drift.
30
- */
31
- export type JwtRoles = {
32
- allRolesAllowed: true;
33
- roles?: never;
34
- } | {
35
- roles: readonly [string, ...string[]];
36
- allRolesAllowed?: never;
37
- };
38
- /**
39
- * JwtRequirement - the {@link JwtRoles} decision PLUS any app-defined authorization fields, e.g.
40
- * `@WpAuthJwt({ allRolesAllowed: true, inOrg: true })`. The framework authenticates (JwtHook.parseJwt)
41
- * and enforces the roles any-of; the app overrides JwtHook.authorizeJwt to enforce its own fields.
42
- * Both hook methods are ASYNC, so an app field like `inOrg` may be answered from a datastore.
43
- *
44
- * This was a SECOND decorator (`@Auth`) whose `roles` was optional — so `@Auth({})` reached the exact
45
- * widest grant that {@link JwtRoles} exists to make un-typeable. Folding it in leaves one decorator per
46
- * credential kind and closes that route by construction.
47
- */
48
- export type JwtRequirement = JwtRoles & {
49
- [field: string]: unknown;
50
- };
51
- /**
52
- * WHERE ONE api-key credential rides on the wire, as a union the COMPILER enforces — the DECLARATION
53
- * a spec generator turns into an OpenAPI `securityScheme`:
54
- *
55
- * ```typescript
56
- * { in: 'header', name: 'x-api-key' } // ✅ → {type: apiKey, in: header, name: x-api-key}
57
- * { in: 'bearer' } // ✅ → {type: http, scheme: bearer}
58
- * { in: 'bearer', name: 'x-api-key' } // ❌ bearer's location IS `Authorization`; a name is a lie
59
- * { in: 'header' } // ❌ a header credential with no header name is unusable
60
- * ```
61
- *
62
- * WHY A UNION AND NOT ONE OPTIONAL `headerName`. The two OpenAPI schemes reachable from "an api key"
63
- * are structurally DIFFERENT documents (`type: apiKey` + `in` + `name` vs `type: http` + `scheme`).
64
- * A single optional field makes the contradictory combination REPRESENTABLE, and a generator handed
65
- * `{in: 'bearer', name: 'x-api-key'}` has to either guess or emit a silently-wrong published contract.
66
- * `name?: never` on the bearer branch is what deletes that state — the same device {@link JwtRoles}
67
- * uses, pinned the same way in `AuthApiKeyCompileAssertions.ts` (a COMPILED file, not a spec).
68
- *
69
- * THIS IS DESCRIPTION, NOT HANDLING. The framework reads no header from this; `ApiKeyHook` still gets
70
- * the whole request and still owns the cross-check. Declaring the location cannot regress running auth
71
- * — it exists so the location stops living ONLY in a hand-written spec fragment that drifts silently
72
- * from the hook's own header constants.
73
- *
74
- * `description` is carried because it is what a docs site renders on its authorization card, and that
75
- * prose belongs beside the declaration rather than in a hand-maintained JSON file.
76
- */
77
1
  export type ApiKeyCredential = {
78
2
  in: 'header';
79
3
  name: string;
@@ -83,48 +7,13 @@ export type ApiKeyCredential = {
83
7
  name?: never;
84
8
  description?: string;
85
9
  };
86
- /**
87
- * EVERY credential one api-key regime requires, in the order they appear in the published document.
88
- *
89
- * A NON-EMPTY tuple, for the same reason {@link JwtRoles}'s `roles` is one: a regime that declares no
90
- * credential would generate a document with no security block — the exact silent failure the
91
- * declaration exists to remove — so `[]` is a COMPILE error rather than a runtime throw (shim shape
92
- * #4). More than one entry is the normal case, not an exotic one: a real regime authenticates a PAIR,
93
- * and every entry here must be presented TOGETHER (an AND, i.e. ONE OpenAPI security-requirement
94
- * object holding every scheme, never a LIST of one-key objects, which would mean "either suffices").
95
- */
96
10
  export type ApiKeyCredentials = readonly [ApiKeyCredential, ...ApiKeyCredential[]];
97
- /**
98
- * The service-to-service / user auth mode of an endpoint. Discriminated union so a filter can
99
- * `switch (mode.kind)` and get the data it needs, exhaustively.
100
- *
101
- * - `public` → no auth check
102
- * - `jwt` → user JWT; `requirement` carries the compiler-enforced role decision
103
- * ({@link JwtRoles}) plus any app-defined authorization fields
104
- * - `oidc` → Google OIDC service-to-service (Cloud Tasks delivery / cross-service RPC);
105
- * `callers` is the allow-list of caller SAs ('self' = this service's SA)
106
- * - `shared-secret` → constant-time compare of a header against the secret bound for `secretKey`
107
- * - `webhook` → an OUTSIDE vendor signed this request its own way; the app's bound `WebhookAuthCallback`
108
- * verifies it, selected by `name`. The framework ships NO vendor crypto (see
109
- * {@link WpAuthWebhook}).
110
- * - `apikey` → a CUSTOMER holds the credential; the app's bound `ApiKeyHook` looks it up
111
- * (async, over the whole header set) and returns the context to seed, selected
112
- * by `regime`. `credentials` DECLARES where the credential rides — an ORDERED,
113
- * non-empty list, because a real regime authenticates a PAIR (a key AND the
114
- * organization id it acts for) that must be presented TOGETHER. NOT a peer
115
- * service — see {@link WpAuthApiKey}.
116
- * - `local-only` → exists ONLY on a developer's machine; not registered and never served when
117
- * {@link RuntimeLocality} says this process is deployed. Authenticates NOBODY —
118
- * it is a deployment gate, not a credential.
119
- */
120
- export type AuthMode = {
121
- kind: 'public';
122
- } | {
11
+ /** One concrete mechanism; user policy belongs exclusively to WpAuthorization. */
12
+ export type AuthMethod = {
123
13
  kind: 'jwt';
124
- requirement: JwtRequirement;
125
14
  } | {
126
15
  kind: 'oidc';
127
- callers: string[];
16
+ callers: readonly string[];
128
17
  } | {
129
18
  kind: 'shared-secret';
130
19
  secretKey: string;
@@ -135,22 +24,25 @@ export type AuthMode = {
135
24
  kind: 'apikey';
136
25
  regime: string;
137
26
  credentials: ApiKeyCredentials;
138
- } | {
139
- kind: 'local-only';
140
27
  };
141
- /**
142
- * Auth metadata attached to a class or method via one of the auth decorators
143
- * (@WpAuthPublic / @WpAuthJwt / @WpAuthOidc / @WpAuthSharedSecret / @WpAuthWebhook / @WpAuthApiKey / @WpAuthLocalOnly) —
144
- * one per credential kind.
145
- *
146
- * Carries a discriminated {@link AuthMode} and nothing else. It USED to also expose
147
- * `authenticated`/`roles` getters "for back-compat with readers that only understand the user-JWT
148
- * model" — deleted, because nothing read them: every reader (AuthFilter, BrowserProxyClient,
149
- * ProxyClient) switches on `mode.kind`, which is the whole point of the discriminated union. A
150
- * flattened view of a union is a second spelling of it, and the flattened one silently answers
151
- * `authenticated: true` for oidc and shared-secret too.
152
- */
28
+ export type AuthMethods = readonly [AuthMethod, ...AuthMethod[]];
29
+ export type AuthMode = AuthMethod | {
30
+ kind: 'public';
31
+ };
32
+ /** Alternatives remain unresolved until a concrete mechanism is chosen for this hop. */
153
33
  export declare class AuthMeta {
154
- mode: AuthMode;
155
- constructor(mode: AuthMode);
34
+ readonly publicReason: string | undefined;
35
+ readonly methods: readonly [AuthMode, ...AuthMode[]];
36
+ constructor(methods: readonly [AuthMode, ...AuthMode[]], publicReason: string | undefined);
37
+ private static snapshot;
38
+ }
39
+ export declare class AuthDeclaration {
40
+ static validate(meta: AuthMeta): void;
41
+ private static validateMethod;
42
+ private static requireText;
156
43
  }
44
+ export declare function jwt(): AuthMethod;
45
+ export declare function oidc(...callers: string[]): AuthMethod;
46
+ export declare function sharedSecret(secretKey: string): AuthMethod;
47
+ export declare function webhook(name: string): AuthMethod;
48
+ export declare function apiKey(regime: string, credentials: ApiKeyCredentials): AuthMethod;
@@ -1,33 +1,107 @@
1
1
  "use strict";
2
- /**
3
- * The TYPE layer of the auth surface: what an endpoint's credential posture IS, with no decorator and
4
- * no reflect-metadata. `decorators.ts` (which attaches these) imports FROM here, never the other way,
5
- * so a reader that only needs to switch on a mode — `DestinationTrust`, `RouteMetadata`, a spec
6
- * generator — does not drag the whole decorator surface in with it.
7
- *
8
- * Split out of `decorators.ts` purely for file size, exactly as `api-kind.ts`, `external-caller.ts` and
9
- * `RouteMetadata.ts` were before it. Nothing about these types' role changed in the move, and the
10
- * barrel keeps the package surface identical.
11
- */
12
2
  Object.defineProperty(exports, "__esModule", { value: true });
13
- exports.AuthMeta = void 0;
14
- /**
15
- * Auth metadata attached to a class or method via one of the auth decorators
16
- * (@WpAuthPublic / @WpAuthJwt / @WpAuthOidc / @WpAuthSharedSecret / @WpAuthWebhook / @WpAuthApiKey / @WpAuthLocalOnly) —
17
- * one per credential kind.
18
- *
19
- * Carries a discriminated {@link AuthMode} and nothing else. It USED to also expose
20
- * `authenticated`/`roles` getters "for back-compat with readers that only understand the user-JWT
21
- * model" — deleted, because nothing read them: every reader (AuthFilter, BrowserProxyClient,
22
- * ProxyClient) switches on `mode.kind`, which is the whole point of the discriminated union. A
23
- * flattened view of a union is a second spelling of it, and the flattened one silently answers
24
- * `authenticated: true` for oidc and shared-secret too.
25
- */
3
+ exports.AuthDeclaration = exports.AuthMeta = void 0;
4
+ exports.jwt = jwt;
5
+ exports.oidc = oidc;
6
+ exports.sharedSecret = sharedSecret;
7
+ exports.webhook = webhook;
8
+ exports.apiKey = apiKey;
9
+ /** Alternatives remain unresolved until a concrete mechanism is chosen for this hop. */
26
10
  class AuthMeta {
27
- mode;
28
- constructor(mode) {
29
- this.mode = mode;
11
+ publicReason;
12
+ methods;
13
+ constructor(methods, publicReason) {
14
+ this.publicReason = publicReason;
15
+ this.methods = methods;
16
+ AuthDeclaration.validate(this);
17
+ this.methods = Object.freeze(methods.map((method) => AuthMeta.snapshot(method)));
18
+ Object.freeze(this);
19
+ }
20
+ // webpieces-disable no-function-outside-class -- stateless contract validation or scope metadata reader used before DI registration
21
+ static snapshot(method) {
22
+ switch (method.kind) {
23
+ case 'oidc': return Object.freeze({ ...method, callers: Object.freeze([...method.callers]) });
24
+ case 'apikey': return Object.freeze({ ...method, credentials: Object.freeze(method.credentials.map((credential) => Object.freeze({ ...credential }))) });
25
+ default: return Object.freeze({ ...method });
26
+ }
30
27
  }
31
28
  }
32
29
  exports.AuthMeta = AuthMeta;
30
+ class AuthDeclaration {
31
+ // webpieces-disable no-function-outside-class -- stateless contract validation or scope metadata reader used before DI registration
32
+ static validate(meta) {
33
+ if (!Array.isArray(meta.methods) || meta.methods.length === 0)
34
+ throw new Error('@WpAuth requires a nonempty methods tuple.');
35
+ const kinds = new Set();
36
+ for (const method of meta.methods) {
37
+ if (!method || typeof method !== 'object' || kinds.has(method.kind))
38
+ throw new Error('@WpAuth rejects duplicate or invalid credential methods.');
39
+ kinds.add(method.kind);
40
+ this.validateMethod(method);
41
+ }
42
+ if (kinds.has('public')) {
43
+ if (meta.methods.length !== 1 || typeof meta.publicReason !== 'string' || !meta.publicReason.trim()) {
44
+ throw new Error('Public auth must be declared separately with @WpAuthPublic(reason).');
45
+ }
46
+ }
47
+ else if (meta.publicReason !== undefined)
48
+ throw new Error('Protected @WpAuth rejects public reasons.');
49
+ }
50
+ // webpieces-disable no-function-outside-class -- stateless contract validation or scope metadata reader used before DI registration
51
+ static validateMethod(method) {
52
+ let keys;
53
+ switch (method.kind) {
54
+ case 'jwt':
55
+ case 'public':
56
+ keys = ['kind'];
57
+ break;
58
+ case 'oidc':
59
+ keys = ['kind', 'callers'];
60
+ if (!Array.isArray(method.callers) || !method.callers.every((caller) => typeof caller === 'string' && caller.trim().length > 0))
61
+ throw new Error('oidc(...) requires valid caller names.');
62
+ break;
63
+ case 'shared-secret':
64
+ keys = ['kind', 'secretKey'];
65
+ this.requireText(method.secretKey, 'sharedSecret');
66
+ break;
67
+ case 'webhook':
68
+ keys = ['kind', 'name'];
69
+ this.requireText(method.name, 'webhook');
70
+ break;
71
+ case 'apikey':
72
+ keys = ['kind', 'regime', 'credentials'];
73
+ this.requireText(method.regime, 'apiKey');
74
+ if (!Array.isArray(method.credentials) || !method.credentials.length)
75
+ throw new Error('apiKey(...) requires a nonempty credentials tuple.');
76
+ for (const credential of method.credentials) {
77
+ if (credential.in === 'header')
78
+ this.requireText(credential.name, 'apiKey header');
79
+ else if (credential.in !== 'bearer' || Object.hasOwn(credential, 'name'))
80
+ throw new Error('Invalid API-key credential location.');
81
+ }
82
+ break;
83
+ default: throw new Error('Unknown @WpAuth method; use jwt/oidc/sharedSecret/webhook/apiKey descriptors.');
84
+ }
85
+ for (const key of Object.keys(method)) {
86
+ if (!keys.includes(key))
87
+ throw new Error(`Auth method rejects '${key}'; operation policy belongs in @WpAuthorization.`);
88
+ }
89
+ }
90
+ // webpieces-disable no-function-outside-class -- stateless contract validation or scope metadata reader used before DI registration
91
+ static requireText(value, label) {
92
+ if (typeof value !== 'string' || !value.trim())
93
+ throw new Error(`${label} requires a nonempty name.`);
94
+ }
95
+ }
96
+ exports.AuthDeclaration = AuthDeclaration;
97
+ // webpieces-disable no-function-outside-class -- canonical credential descriptor factory
98
+ function jwt() { return { kind: 'jwt' }; }
99
+ // webpieces-disable no-function-outside-class -- canonical credential descriptor factory
100
+ function oidc(...callers) { return { kind: 'oidc', callers }; }
101
+ // webpieces-disable no-function-outside-class -- canonical credential descriptor factory
102
+ function sharedSecret(secretKey) { return { kind: 'shared-secret', secretKey }; }
103
+ // webpieces-disable no-function-outside-class -- canonical credential descriptor factory
104
+ function webhook(name) { return { kind: 'webhook', name }; }
105
+ // webpieces-disable no-function-outside-class -- canonical credential descriptor factory
106
+ function apiKey(regime, credentials) { return { kind: 'apikey', regime, credentials }; }
33
107
  //# sourceMappingURL=auth-mode.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"auth-mode.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/auth-mode.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;;AAiHH;;;;;;;;;;;GAWG;AACH,MAAa,QAAQ;IACjB,IAAI,CAAW;IAEf,YAAY,IAAc;QACtB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACrB,CAAC;CACJ;AAND,4BAMC","sourcesContent":["/**\n * The TYPE layer of the auth surface: what an endpoint's credential posture IS, with no decorator and\n * no reflect-metadata. `decorators.ts` (which attaches these) imports FROM here, never the other way,\n * so a reader that only needs to switch on a mode — `DestinationTrust`, `RouteMetadata`, a spec\n * generator — does not drag the whole decorator surface in with it.\n *\n * Split out of `decorators.ts` purely for file size, exactly as `api-kind.ts`, `external-caller.ts` and\n * `RouteMetadata.ts` were before it. Nothing about these types' role changed in the move, and the\n * barrel keeps the package surface identical.\n */\n\n/**\n * The role decision for a JWT endpoint, as a union the COMPILER enforces — one spelling per decision,\n * every broken combination a compile error:\n *\n * ```typescript\n * @WpAuthJwt({ roles: ['admin'] }) // ✅ role-gated (any-of)\n * @WpAuthJwt({ allRolesAllowed: true }) // ✅ every authenticated user, said out loud\n * @WpAuthJwt({}) // ❌ pick a branch\n * @WpAuthJwt({ roles: [] }) // ❌ needs at least one role\n * ```\n *\n * `allRolesAllowed` exists ONLY on the wide branch (the dangerous half must be a greppable token, and\n * the narrow branch rejects it as a redundant second spelling); `roles` is a NON-EMPTY tuple so\n * \"declared roles, passed none\" — the old optional `string[]`'s silent widest grant — cannot be written.\n * All six bad cases are pinned in `AuthJwtCompileAssertions.ts` — a COMPILED file, not a spec: tsc\n * fails the build (TS2578) if any starts compiling. A spec cannot do this (see that file's header).\n *\n * WHY a type rather than the runtime `throw` this replaced: `.claude/review/backwards-compatibility.md`\n * shim shapes #4 and #5. Not restated here — three copies of one rationale is three things to drift.\n */\nexport type JwtRoles =\n | { allRolesAllowed: true; roles?: never }\n | { roles: readonly [string, ...string[]]; allRolesAllowed?: never };\n\n/**\n * JwtRequirement - the {@link JwtRoles} decision PLUS any app-defined authorization fields, e.g.\n * `@WpAuthJwt({ allRolesAllowed: true, inOrg: true })`. The framework authenticates (JwtHook.parseJwt)\n * and enforces the roles any-of; the app overrides JwtHook.authorizeJwt to enforce its own fields.\n * Both hook methods are ASYNC, so an app field like `inOrg` may be answered from a datastore.\n *\n * This was a SECOND decorator (`@Auth`) whose `roles` was optional — so `@Auth({})` reached the exact\n * widest grant that {@link JwtRoles} exists to make un-typeable. Folding it in leaves one decorator per\n * credential kind and closes that route by construction.\n */\n// webpieces-disable no-any-unknown -- app-defined authorization fields (inOrg, tenant, ...)\nexport type JwtRequirement = JwtRoles & { [field: string]: unknown };\n\n/**\n * WHERE ONE api-key credential rides on the wire, as a union the COMPILER enforces — the DECLARATION\n * a spec generator turns into an OpenAPI `securityScheme`:\n *\n * ```typescript\n * { in: 'header', name: 'x-api-key' } // ✅ → {type: apiKey, in: header, name: x-api-key}\n * { in: 'bearer' } // ✅ → {type: http, scheme: bearer}\n * { in: 'bearer', name: 'x-api-key' } // ❌ bearer's location IS `Authorization`; a name is a lie\n * { in: 'header' } // ❌ a header credential with no header name is unusable\n * ```\n *\n * WHY A UNION AND NOT ONE OPTIONAL `headerName`. The two OpenAPI schemes reachable from \"an api key\"\n * are structurally DIFFERENT documents (`type: apiKey` + `in` + `name` vs `type: http` + `scheme`).\n * A single optional field makes the contradictory combination REPRESENTABLE, and a generator handed\n * `{in: 'bearer', name: 'x-api-key'}` has to either guess or emit a silently-wrong published contract.\n * `name?: never` on the bearer branch is what deletes that state — the same device {@link JwtRoles}\n * uses, pinned the same way in `AuthApiKeyCompileAssertions.ts` (a COMPILED file, not a spec).\n *\n * THIS IS DESCRIPTION, NOT HANDLING. The framework reads no header from this; `ApiKeyHook` still gets\n * the whole request and still owns the cross-check. Declaring the location cannot regress running auth\n * — it exists so the location stops living ONLY in a hand-written spec fragment that drifts silently\n * from the hook's own header constants.\n *\n * `description` is carried because it is what a docs site renders on its authorization card, and that\n * prose belongs beside the declaration rather than in a hand-maintained JSON file.\n */\nexport type ApiKeyCredential =\n | { in: 'header'; name: string; description?: string }\n | { in: 'bearer'; name?: never; description?: string };\n\n/**\n * EVERY credential one api-key regime requires, in the order they appear in the published document.\n *\n * A NON-EMPTY tuple, for the same reason {@link JwtRoles}'s `roles` is one: a regime that declares no\n * credential would generate a document with no security block — the exact silent failure the\n * declaration exists to remove — so `[]` is a COMPILE error rather than a runtime throw (shim shape\n * #4). More than one entry is the normal case, not an exotic one: a real regime authenticates a PAIR,\n * and every entry here must be presented TOGETHER (an AND, i.e. ONE OpenAPI security-requirement\n * object holding every scheme, never a LIST of one-key objects, which would mean \"either suffices\").\n */\nexport type ApiKeyCredentials = readonly [ApiKeyCredential, ...ApiKeyCredential[]];\n\n/**\n * The service-to-service / user auth mode of an endpoint. Discriminated union so a filter can\n * `switch (mode.kind)` and get the data it needs, exhaustively.\n *\n * - `public` → no auth check\n * - `jwt` → user JWT; `requirement` carries the compiler-enforced role decision\n * ({@link JwtRoles}) plus any app-defined authorization fields\n * - `oidc` → Google OIDC service-to-service (Cloud Tasks delivery / cross-service RPC);\n * `callers` is the allow-list of caller SAs ('self' = this service's SA)\n * - `shared-secret` → constant-time compare of a header against the secret bound for `secretKey`\n * - `webhook` → an OUTSIDE vendor signed this request its own way; the app's bound `WebhookAuthCallback`\n * verifies it, selected by `name`. The framework ships NO vendor crypto (see\n * {@link WpAuthWebhook}).\n * - `apikey` → a CUSTOMER holds the credential; the app's bound `ApiKeyHook` looks it up\n * (async, over the whole header set) and returns the context to seed, selected\n * by `regime`. `credentials` DECLARES where the credential rides — an ORDERED,\n * non-empty list, because a real regime authenticates a PAIR (a key AND the\n * organization id it acts for) that must be presented TOGETHER. NOT a peer\n * service — see {@link WpAuthApiKey}.\n * - `local-only` → exists ONLY on a developer's machine; not registered and never served when\n * {@link RuntimeLocality} says this process is deployed. Authenticates NOBODY —\n * it is a deployment gate, not a credential.\n */\nexport type AuthMode =\n | { kind: 'public' }\n | { kind: 'jwt'; requirement: JwtRequirement }\n | { kind: 'oidc'; callers: string[] }\n | { kind: 'shared-secret'; secretKey: string }\n | { kind: 'webhook'; name: string }\n | { kind: 'apikey'; regime: string; credentials: ApiKeyCredentials }\n | { kind: 'local-only' };\n\n/**\n * Auth metadata attached to a class or method via one of the auth decorators\n * (@WpAuthPublic / @WpAuthJwt / @WpAuthOidc / @WpAuthSharedSecret / @WpAuthWebhook / @WpAuthApiKey / @WpAuthLocalOnly) —\n * one per credential kind.\n *\n * Carries a discriminated {@link AuthMode} and nothing else. It USED to also expose\n * `authenticated`/`roles` getters \"for back-compat with readers that only understand the user-JWT\n * model\" — deleted, because nothing read them: every reader (AuthFilter, BrowserProxyClient,\n * ProxyClient) switches on `mode.kind`, which is the whole point of the discriminated union. A\n * flattened view of a union is a second spelling of it, and the flattened one silently answers\n * `authenticated: true` for oidc and shared-secret too.\n */\nexport class AuthMeta {\n mode: AuthMode;\n\n constructor(mode: AuthMode) {\n this.mode = mode;\n }\n}\n"]}
1
+ {"version":3,"file":"auth-mode.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/auth-mode.ts"],"names":[],"mappings":"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","sourcesContent":["export type ApiKeyCredential =\n | { in: 'header'; name: string; description?: string }\n | { in: 'bearer'; name?: never; description?: string };\n\nexport type ApiKeyCredentials = readonly [ApiKeyCredential, ...ApiKeyCredential[]];\n\n/** One concrete mechanism; user policy belongs exclusively to WpAuthorization. */\nexport type AuthMethod =\n | { kind: 'jwt' }\n | { kind: 'oidc'; callers: readonly string[] }\n | { kind: 'shared-secret'; secretKey: string }\n | { kind: 'webhook'; name: string }\n | { kind: 'apikey'; regime: string; credentials: ApiKeyCredentials };\n\nexport type AuthMethods = readonly [AuthMethod, ...AuthMethod[]];\nexport type AuthMode = AuthMethod | { kind: 'public' };\n\n/** Alternatives remain unresolved until a concrete mechanism is chosen for this hop. */\nexport class AuthMeta {\n readonly methods: readonly [AuthMode, ...AuthMode[]];\n\n constructor(\n methods: readonly [AuthMode, ...AuthMode[]],\n public readonly publicReason: string | undefined,\n ) {\n this.methods = methods;\n AuthDeclaration.validate(this);\n this.methods = Object.freeze(methods.map((method: AuthMode) => AuthMeta.snapshot(method))) as readonly [AuthMode, ...AuthMode[]];\n Object.freeze(this);\n }\n\n // webpieces-disable no-function-outside-class -- stateless contract validation or scope metadata reader used before DI registration\n private static snapshot(method: AuthMode): AuthMode {\n switch (method.kind) {\n case 'oidc': return Object.freeze({ ...method, callers: Object.freeze([...method.callers]) });\n case 'apikey': return Object.freeze({ ...method, credentials: Object.freeze(method.credentials.map((credential: ApiKeyCredential) => Object.freeze({ ...credential }))) as ApiKeyCredentials });\n default: return Object.freeze({ ...method });\n }\n }\n}\n\nexport class AuthDeclaration {\n // webpieces-disable no-function-outside-class -- stateless contract validation or scope metadata reader used before DI registration\n static validate(meta: AuthMeta): void {\n if (!Array.isArray(meta.methods) || meta.methods.length === 0) throw new Error('@WpAuth requires a nonempty methods tuple.');\n const kinds = new Set<string>();\n for (const method of meta.methods) {\n if (!method || typeof method !== 'object' || kinds.has(method.kind)) throw new Error('@WpAuth rejects duplicate or invalid credential methods.');\n kinds.add(method.kind);\n this.validateMethod(method);\n }\n if (kinds.has('public')) {\n if (meta.methods.length !== 1 || typeof meta.publicReason !== 'string' || !meta.publicReason.trim()) {\n throw new Error('Public auth must be declared separately with @WpAuthPublic(reason).');\n }\n } else if (meta.publicReason !== undefined) throw new Error('Protected @WpAuth rejects public reasons.');\n }\n\n // webpieces-disable no-function-outside-class -- stateless contract validation or scope metadata reader used before DI registration\n private static validateMethod(method: AuthMode): void {\n let keys: readonly string[];\n switch (method.kind) {\n case 'jwt':\n case 'public': keys = ['kind']; break;\n case 'oidc':\n keys = ['kind', 'callers'];\n if (!Array.isArray(method.callers) || !method.callers.every((caller: string) => typeof caller === 'string' && caller.trim().length > 0)) throw new Error('oidc(...) requires valid caller names.');\n break;\n case 'shared-secret':\n keys = ['kind', 'secretKey'];\n this.requireText(method.secretKey, 'sharedSecret'); break;\n case 'webhook':\n keys = ['kind', 'name'];\n this.requireText(method.name, 'webhook'); break;\n case 'apikey':\n keys = ['kind', 'regime', 'credentials'];\n this.requireText(method.regime, 'apiKey');\n if (!Array.isArray(method.credentials) || !method.credentials.length) throw new Error('apiKey(...) requires a nonempty credentials tuple.');\n for (const credential of method.credentials) {\n if (credential.in === 'header') this.requireText(credential.name, 'apiKey header');\n else if (credential.in !== 'bearer' || Object.hasOwn(credential, 'name')) throw new Error('Invalid API-key credential location.');\n }\n break;\n default: throw new Error('Unknown @WpAuth method; use jwt/oidc/sharedSecret/webhook/apiKey descriptors.');\n }\n for (const key of Object.keys(method)) {\n if (!keys.includes(key)) throw new Error(`Auth method rejects '${key}'; operation policy belongs in @WpAuthorization.`);\n }\n }\n\n // webpieces-disable no-function-outside-class -- stateless contract validation or scope metadata reader used before DI registration\n private static requireText(value: string, label: string): void {\n if (typeof value !== 'string' || !value.trim()) throw new Error(`${label} requires a nonempty name.`);\n }\n}\n\n// webpieces-disable no-function-outside-class -- canonical credential descriptor factory\nexport function jwt(): AuthMethod { return { kind: 'jwt' }; }\n// webpieces-disable no-function-outside-class -- canonical credential descriptor factory\nexport function oidc(...callers: string[]): AuthMethod { return { kind: 'oidc', callers }; }\n// webpieces-disable no-function-outside-class -- canonical credential descriptor factory\nexport function sharedSecret(secretKey: string): AuthMethod { return { kind: 'shared-secret', secretKey }; }\n// webpieces-disable no-function-outside-class -- canonical credential descriptor factory\nexport function webhook(name: string): AuthMethod { return { kind: 'webhook', name }; }\n// webpieces-disable no-function-outside-class -- canonical credential descriptor factory\nexport function apiKey(regime: string, credentials: ApiKeyCredentials): AuthMethod { return { kind: 'apikey', regime, credentials }; }\n"]}
@@ -0,0 +1,38 @@
1
+ import 'reflect-metadata';
2
+ export declare enum AuthorizationType {
3
+ ALL_USERS = "all-users",
4
+ ROLES = "roles",
5
+ SERVICE_ONLY = "service-only",
6
+ USERS_OR_SERVICES = "users-or-services",
7
+ ANONYMOUS = "anonymous",
8
+ CUSTOM = "custom"
9
+ }
10
+ export type AuthorizationRequirement<TPolicy = never> = {
11
+ authType: AuthorizationType.ALL_USERS | AuthorizationType.SERVICE_ONLY | AuthorizationType.USERS_OR_SERVICES;
12
+ roles?: never;
13
+ reason?: never;
14
+ appPolicy?: never;
15
+ } | {
16
+ authType: AuthorizationType.ROLES;
17
+ roles: readonly [string, ...string[]];
18
+ reason?: never;
19
+ appPolicy?: never;
20
+ } | {
21
+ authType: AuthorizationType.ANONYMOUS;
22
+ reason: string;
23
+ roles?: never;
24
+ appPolicy?: never;
25
+ } | {
26
+ authType: AuthorizationType.CUSTOM;
27
+ appPolicy: TPolicy;
28
+ roles?: never;
29
+ reason?: never;
30
+ };
31
+ export declare const AUTHORIZATION_METADATA_KEY = "webpieces:authorization";
32
+ export declare const AUTHORIZATION_METHODS_KEY = "webpieces:authorization-methods";
33
+ /** Runtime checks also protect JavaScript and type-erased callers. */
34
+ export declare class AuthorizationDeclaration {
35
+ static validate(value: AuthorizationRequirement<unknown>): void;
36
+ }
37
+ export declare function WpAuthorization<TPolicy = never>(policy: AuthorizationRequirement<TPolicy>): MethodDecorator;
38
+ export declare function getAuthorization(apiClass: Function, methodName: string): AuthorizationRequirement<unknown> | undefined;