@webpieces/http-routing 0.3.352 → 0.3.353

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpieces/http-routing",
3
- "version": "0.3.352",
3
+ "version": "0.3.353",
4
4
  "description": "Decorator-based routing with auto-wiring for WebPieces",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -22,9 +22,9 @@
22
22
  },
23
23
  "dependencies": {
24
24
  "@inversifyjs/binding-decorators": "1.1.5",
25
- "@webpieces/core-context": "0.3.352",
26
- "@webpieces/core-util": "0.3.352",
27
- "@webpieces/gcp-identity": "0.3.352",
25
+ "@webpieces/core-context": "0.3.353",
26
+ "@webpieces/core-util": "0.3.353",
27
+ "@webpieces/gcp-identity": "0.3.353",
28
28
  "inversify": "7.10.4",
29
29
  "jsonwebtoken": "9.0.2",
30
30
  "minimatch": "10.0.1"
@@ -27,8 +27,9 @@ export declare abstract class JwtHook {
27
27
  * by type; rebindable in tests) ONLY to customize the caller policy — e.g. an app that reads an
28
28
  * `ALLOWED_OIDC_CALLERS` env var at its composition root and enforces that allow-list. When NO
29
29
  * OidcHook is bound, the framework {@link AuthFilter} runs the built-in {@link DefaultOidcVerifier}
30
- * directly, so a server that wires nothing still verifies Google OIDC from its `@AuthOidc(...callers)`
31
- * (else `['self']`). `verifyOidc` verifies the token against `callers`; throw on failure.
30
+ * directly, so a server that wires nothing still verifies Google OIDC against its `@AuthOidc(...callers)`
31
+ * (else trusts the edge — any Google-signed caller). `verifyOidc` verifies the token against `callers`;
32
+ * throw on failure.
32
33
  */
33
34
  export declare abstract class OidcHook {
34
35
  abstract verifyOidc(token: string, callers: string[]): Promise<void>;
package/src/AuthHooks.js CHANGED
@@ -33,8 +33,9 @@ exports.JwtHook = JwtHook;
33
33
  * by type; rebindable in tests) ONLY to customize the caller policy — e.g. an app that reads an
34
34
  * `ALLOWED_OIDC_CALLERS` env var at its composition root and enforces that allow-list. When NO
35
35
  * OidcHook is bound, the framework {@link AuthFilter} runs the built-in {@link DefaultOidcVerifier}
36
- * directly, so a server that wires nothing still verifies Google OIDC from its `@AuthOidc(...callers)`
37
- * (else `['self']`). `verifyOidc` verifies the token against `callers`; throw on failure.
36
+ * directly, so a server that wires nothing still verifies Google OIDC against its `@AuthOidc(...callers)`
37
+ * (else trusts the edge — any Google-signed caller). `verifyOidc` verifies the token against `callers`;
38
+ * throw on failure.
38
39
  */
39
40
  class OidcHook {
40
41
  }
@@ -1 +1 @@
1
- {"version":3,"file":"AuthHooks.js","sourceRoot":"","sources":["../../../../../packages/http/http-routing/src/AuthHooks.ts"],"names":[],"mappings":";;;AAAA,oDAA0E;AAG1E;;;;;;;;;;;;GAYG;AACH,MAAsB,OAAO;IAIzB;;;OAGG;IACH,YAAY,CAAC,MAAkB,EAAE,WAA2B;QACxD,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE,CAAC;QACtC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAY,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;YACjF,MAAM,IAAI,8BAAkB,CAAC,mCAAmC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACxF,CAAC;IACL,CAAC;CACJ;AAdD,0BAcC;AAED;;;;;;;GAOG;AACH,MAAsB,QAAQ;CAE7B;AAFD,4BAEC","sourcesContent":["import { JwtRequirement, HttpForbiddenError } from '@webpieces/core-util';\nimport { AuthValues } from './AuthConfig';\n\n/**\n * JwtHook - the OPTIONAL user-JWT mechanism. Bind one (inject by type, per no-symbol-di-tokens;\n * rebindable in tests) to turn on `@AuthJwt(...)` endpoints. When NO JwtHook is bound, the framework\n * {@link AuthFilter} treats every jwt endpoint as \"not enabled\" and fails fast (401) — there is no\n * default JWT verification because it needs an app secret + payload shape the framework can't guess.\n *\n * - `parseJwt` — AUTHENTICATION: decode/verify a user JWT into {@link AuthValues}, or throw. The\n * app owns the strategy (HS256 secret, RS256 + JWKS, a provider SDK, ...).\n * - `authorizeJwt` — AUTHORIZATION: check the authenticated user against the endpoint's\n * {@link JwtRequirement}. The DEFAULT enforces `roles` (any-of; empty = any\n * authenticated user); override for app-defined requirements carried by\n * `@Auth({...})` — e.g. `if (requirement['inOrg'] && !values.claims['orgId']) ...`.\n */\nexport abstract class JwtHook {\n /** Parse a user JWT (kind:'jwt') — AUTHENTICATION only. Return who the user is, or throw. */\n abstract parseJwt(token: string): AuthValues;\n\n /**\n * DEFAULT authorization: enforce `roles` (any-of; empty = any authenticated user). Override to\n * enforce app-defined requirements. Throw HttpForbiddenError to deny; return to allow.\n */\n authorizeJwt(values: AuthValues, requirement: JwtRequirement): void {\n const roles = requirement.roles ?? [];\n if (roles.length > 0 && !roles.some((role: string) => values.roles.includes(role))) {\n throw new HttpForbiddenError(`Endpoint requires one of roles: ${roles.join(', ')}`);\n }\n }\n}\n\n/**\n * OidcHook - the OPTIONAL override for Google OIDC service-to-service verification. Bind one (inject\n * by type; rebindable in tests) ONLY to customize the caller policy — e.g. an app that reads an\n * `ALLOWED_OIDC_CALLERS` env var at its composition root and enforces that allow-list. When NO\n * OidcHook is bound, the framework {@link AuthFilter} runs the built-in {@link DefaultOidcVerifier}\n * directly, so a server that wires nothing still verifies Google OIDC from its `@AuthOidc(...callers)`\n * (else `['self']`). `verifyOidc` verifies the token against `callers`; throw on failure.\n */\nexport abstract class OidcHook {\n abstract verifyOidc(token: string, callers: string[]): Promise<void>;\n}\n"]}
1
+ {"version":3,"file":"AuthHooks.js","sourceRoot":"","sources":["../../../../../packages/http/http-routing/src/AuthHooks.ts"],"names":[],"mappings":";;;AAAA,oDAA0E;AAG1E;;;;;;;;;;;;GAYG;AACH,MAAsB,OAAO;IAIzB;;;OAGG;IACH,YAAY,CAAC,MAAkB,EAAE,WAA2B;QACxD,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE,CAAC;QACtC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAY,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;YACjF,MAAM,IAAI,8BAAkB,CAAC,mCAAmC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACxF,CAAC;IACL,CAAC;CACJ;AAdD,0BAcC;AAED;;;;;;;;GAQG;AACH,MAAsB,QAAQ;CAE7B;AAFD,4BAEC","sourcesContent":["import { JwtRequirement, HttpForbiddenError } from '@webpieces/core-util';\nimport { AuthValues } from './AuthConfig';\n\n/**\n * JwtHook - the OPTIONAL user-JWT mechanism. Bind one (inject by type, per no-symbol-di-tokens;\n * rebindable in tests) to turn on `@AuthJwt(...)` endpoints. When NO JwtHook is bound, the framework\n * {@link AuthFilter} treats every jwt endpoint as \"not enabled\" and fails fast (401) — there is no\n * default JWT verification because it needs an app secret + payload shape the framework can't guess.\n *\n * - `parseJwt` — AUTHENTICATION: decode/verify a user JWT into {@link AuthValues}, or throw. The\n * app owns the strategy (HS256 secret, RS256 + JWKS, a provider SDK, ...).\n * - `authorizeJwt` — AUTHORIZATION: check the authenticated user against the endpoint's\n * {@link JwtRequirement}. The DEFAULT enforces `roles` (any-of; empty = any\n * authenticated user); override for app-defined requirements carried by\n * `@Auth({...})` — e.g. `if (requirement['inOrg'] && !values.claims['orgId']) ...`.\n */\nexport abstract class JwtHook {\n /** Parse a user JWT (kind:'jwt') — AUTHENTICATION only. Return who the user is, or throw. */\n abstract parseJwt(token: string): AuthValues;\n\n /**\n * DEFAULT authorization: enforce `roles` (any-of; empty = any authenticated user). Override to\n * enforce app-defined requirements. Throw HttpForbiddenError to deny; return to allow.\n */\n authorizeJwt(values: AuthValues, requirement: JwtRequirement): void {\n const roles = requirement.roles ?? [];\n if (roles.length > 0 && !roles.some((role: string) => values.roles.includes(role))) {\n throw new HttpForbiddenError(`Endpoint requires one of roles: ${roles.join(', ')}`);\n }\n }\n}\n\n/**\n * OidcHook - the OPTIONAL override for Google OIDC service-to-service verification. Bind one (inject\n * by type; rebindable in tests) ONLY to customize the caller policy — e.g. an app that reads an\n * `ALLOWED_OIDC_CALLERS` env var at its composition root and enforces that allow-list. When NO\n * OidcHook is bound, the framework {@link AuthFilter} runs the built-in {@link DefaultOidcVerifier}\n * directly, so a server that wires nothing still verifies Google OIDC against its `@AuthOidc(...callers)`\n * (else trusts the edge — any Google-signed caller). `verifyOidc` verifies the token against `callers`;\n * throw on failure.\n */\nexport abstract class OidcHook {\n abstract verifyOidc(token: string, callers: string[]): Promise<void>;\n}\n"]}
@@ -4,11 +4,14 @@
4
4
  * OIDC "just work" with ZERO wiring: http-routing depends on @webpieces/gcp-identity ON PURPOSE so a
5
5
  * server that binds nothing still verifies service-to-service OIDC.
6
6
  *
7
- * `verify` checks the token against the endpoint's `@AuthOidc(...callers)` allow-list, falling back
8
- * to `['self']` (this service's own runtime SA) when the endpoint named none. Off-GCP, gcp-identity
9
- * mints + accepts a dev token so local dev needs no GCP. Framework code reads NO process.env — an app
10
- * that wants an env-driven allow-list binds an {@link OidcHook} instead (env read at its composition
11
- * root), which keeps this default env-free and tests parallel-safe.
7
+ * `verify` honors the endpoint's `@AuthOidc(...callers)` contract exactly: EMPTY (a bare `@AuthOidc()`)
8
+ * = TRUST THE EDGE verify the token is genuinely Google-signed and let the deployment's `run.invoker`
9
+ * IAM restrict WHO (gcp-identity warns loudly, once, if the service is actually public and thus the
10
+ * edge is NOT the gate). A non-empty list (`@AuthOidc('svc-a')`) enforces that explicit app-level
11
+ * allow-list as defense-in-depth. Off-GCP, gcp-identity mints + accepts a dev token so local dev needs
12
+ * no GCP. Framework code reads NO process.env — an app that wants an env-driven allow-list binds an
13
+ * {@link OidcHook} instead (env read at its composition root), keeping this default env-free and tests
14
+ * parallel-safe.
12
15
  */
13
16
  export declare class DefaultOidcVerifier {
14
17
  verify(token: string, callers: string[]): Promise<void>;
@@ -12,16 +12,21 @@ const gcp_identity_1 = require("@webpieces/gcp-identity");
12
12
  * OIDC "just work" with ZERO wiring: http-routing depends on @webpieces/gcp-identity ON PURPOSE so a
13
13
  * server that binds nothing still verifies service-to-service OIDC.
14
14
  *
15
- * `verify` checks the token against the endpoint's `@AuthOidc(...callers)` allow-list, falling back
16
- * to `['self']` (this service's own runtime SA) when the endpoint named none. Off-GCP, gcp-identity
17
- * mints + accepts a dev token so local dev needs no GCP. Framework code reads NO process.env — an app
18
- * that wants an env-driven allow-list binds an {@link OidcHook} instead (env read at its composition
19
- * root), which keeps this default env-free and tests parallel-safe.
15
+ * `verify` honors the endpoint's `@AuthOidc(...callers)` contract exactly: EMPTY (a bare `@AuthOidc()`)
16
+ * = TRUST THE EDGE verify the token is genuinely Google-signed and let the deployment's `run.invoker`
17
+ * IAM restrict WHO (gcp-identity warns loudly, once, if the service is actually public and thus the
18
+ * edge is NOT the gate). A non-empty list (`@AuthOidc('svc-a')`) enforces that explicit app-level
19
+ * allow-list as defense-in-depth. Off-GCP, gcp-identity mints + accepts a dev token so local dev needs
20
+ * no GCP. Framework code reads NO process.env — an app that wants an env-driven allow-list binds an
21
+ * {@link OidcHook} instead (env read at its composition root), keeping this default env-free and tests
22
+ * parallel-safe.
20
23
  */
21
24
  let DefaultOidcVerifier = class DefaultOidcVerifier {
22
25
  async verify(token, callers) {
23
- const allow = callers.length > 0 ? callers : ['self'];
24
- const result = await (0, gcp_identity_1.verifyOidcFromCallers)(token, allow);
26
+ // callers pass straight through: EMPTY (@AuthOidc() with no callers) = TRUST THE EDGE, a
27
+ // non-empty list enforces the explicit allow-list. Do NOT inject a ['self'] default — that
28
+ // would reject a legitimate cross-SA caller the edge already admitted.
29
+ const result = await (0, gcp_identity_1.verifyOidcFromCallers)(token, callers);
25
30
  if (!result.ok) {
26
31
  throw new core_util_1.HttpUnauthorizedError(`OIDC rejected: ${result.reason ?? 'not an allowed caller'}`);
27
32
  }
@@ -1 +1 @@
1
- {"version":3,"file":"DefaultOidcVerifier.js","sourceRoot":"","sources":["../../../../../packages/http/http-routing/src/DefaultOidcVerifier.ts"],"names":[],"mappings":";;;;AAAA,yCAAuC;AACvC,0DAAoE;AACpE,oDAA6D;AAC7D,0DAAgE;AAEhE;;;;;;;;;;;GAWG;AAGI,IAAM,mBAAmB,GAAzB,MAAM,mBAAmB;IAC5B,KAAK,CAAC,MAAM,CAAC,KAAa,EAAE,OAAiB;QACzC,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QACtD,MAAM,MAAM,GAAG,MAAM,IAAA,oCAAqB,EAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QACzD,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;YACb,MAAM,IAAI,iCAAqB,CAAC,kBAAkB,MAAM,CAAC,MAAM,IAAI,uBAAuB,EAAE,CAAC,CAAC;QAClG,CAAC;IACL,CAAC;CACJ,CAAA;AARY,kDAAmB;8BAAnB,mBAAmB;IAF/B,IAAA,wCAAyB,GAAE;IAC3B,IAAA,sBAAU,GAAE;GACA,mBAAmB,CAQ/B","sourcesContent":["import { injectable } from 'inversify';\nimport { provideFrameworkSingleton } from '@webpieces/core-context';\nimport { HttpUnauthorizedError } from '@webpieces/core-util';\nimport { verifyOidcFromCallers } from '@webpieces/gcp-identity';\n\n/**\n * DefaultOidcVerifier - the framework's built-in Google OIDC verifier, injected into\n * {@link AuthFilter} and run directly whenever no app {@link OidcHook} is bound. It is what makes\n * OIDC \"just work\" with ZERO wiring: http-routing depends on @webpieces/gcp-identity ON PURPOSE so a\n * server that binds nothing still verifies service-to-service OIDC.\n *\n * `verify` checks the token against the endpoint's `@AuthOidc(...callers)` allow-list, falling back\n * to `['self']` (this service's own runtime SA) when the endpoint named none. Off-GCP, gcp-identity\n * mints + accepts a dev token so local dev needs no GCP. Framework code reads NO process.env — an app\n * that wants an env-driven allow-list binds an {@link OidcHook} instead (env read at its composition\n * root), which keeps this default env-free and tests parallel-safe.\n */\n@provideFrameworkSingleton()\n@injectable()\nexport class DefaultOidcVerifier {\n async verify(token: string, callers: string[]): Promise<void> {\n const allow = callers.length > 0 ? callers : ['self'];\n const result = await verifyOidcFromCallers(token, allow);\n if (!result.ok) {\n throw new HttpUnauthorizedError(`OIDC rejected: ${result.reason ?? 'not an allowed caller'}`);\n }\n }\n}\n"]}
1
+ {"version":3,"file":"DefaultOidcVerifier.js","sourceRoot":"","sources":["../../../../../packages/http/http-routing/src/DefaultOidcVerifier.ts"],"names":[],"mappings":";;;;AAAA,yCAAuC;AACvC,0DAAoE;AACpE,oDAA6D;AAC7D,0DAAgE;AAEhE;;;;;;;;;;;;;;GAcG;AAGI,IAAM,mBAAmB,GAAzB,MAAM,mBAAmB;IAC5B,KAAK,CAAC,MAAM,CAAC,KAAa,EAAE,OAAiB;QACzC,yFAAyF;QACzF,2FAA2F;QAC3F,uEAAuE;QACvE,MAAM,MAAM,GAAG,MAAM,IAAA,oCAAqB,EAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QAC3D,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;YACb,MAAM,IAAI,iCAAqB,CAAC,kBAAkB,MAAM,CAAC,MAAM,IAAI,uBAAuB,EAAE,CAAC,CAAC;QAClG,CAAC;IACL,CAAC;CACJ,CAAA;AAVY,kDAAmB;8BAAnB,mBAAmB;IAF/B,IAAA,wCAAyB,GAAE;IAC3B,IAAA,sBAAU,GAAE;GACA,mBAAmB,CAU/B","sourcesContent":["import { injectable } from 'inversify';\nimport { provideFrameworkSingleton } from '@webpieces/core-context';\nimport { HttpUnauthorizedError } from '@webpieces/core-util';\nimport { verifyOidcFromCallers } from '@webpieces/gcp-identity';\n\n/**\n * DefaultOidcVerifier - the framework's built-in Google OIDC verifier, injected into\n * {@link AuthFilter} and run directly whenever no app {@link OidcHook} is bound. It is what makes\n * OIDC \"just work\" with ZERO wiring: http-routing depends on @webpieces/gcp-identity ON PURPOSE so a\n * server that binds nothing still verifies service-to-service OIDC.\n *\n * `verify` honors the endpoint's `@AuthOidc(...callers)` contract exactly: EMPTY (a bare `@AuthOidc()`)\n * = TRUST THE EDGE — verify the token is genuinely Google-signed and let the deployment's `run.invoker`\n * IAM restrict WHO (gcp-identity warns loudly, once, if the service is actually public and thus the\n * edge is NOT the gate). A non-empty list (`@AuthOidc('svc-a')`) enforces that explicit app-level\n * allow-list as defense-in-depth. Off-GCP, gcp-identity mints + accepts a dev token so local dev needs\n * no GCP. Framework code reads NO process.env — an app that wants an env-driven allow-list binds an\n * {@link OidcHook} instead (env read at its composition root), keeping this default env-free and tests\n * parallel-safe.\n */\n@provideFrameworkSingleton()\n@injectable()\nexport class DefaultOidcVerifier {\n async verify(token: string, callers: string[]): Promise<void> {\n // callers pass straight through: EMPTY (@AuthOidc() with no callers) = TRUST THE EDGE, a\n // non-empty list enforces the explicit allow-list. Do NOT inject a ['self'] default — that\n // would reject a legitimate cross-SA caller the edge already admitted.\n const result = await verifyOidcFromCallers(token, callers);\n if (!result.ok) {\n throw new HttpUnauthorizedError(`OIDC rejected: ${result.reason ?? 'not an allowed caller'}`);\n }\n }\n}\n"]}