@venizia/ignis 0.2.0-1 → 0.2.0-3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -1
- package/dist/base/applications/server.d.ts +1 -1
- package/dist/base/applications/server.d.ts.map +1 -1
- package/dist/base/applications/server.js +1 -1
- package/dist/base/applications/server.js.map +1 -1
- package/dist/base/controllers/grpc/abstract.d.ts +1 -1
- package/dist/base/controllers/grpc/abstract.d.ts.map +1 -1
- package/dist/base/controllers/grpc/abstract.js +12 -10
- package/dist/base/controllers/grpc/abstract.js.map +1 -1
- package/dist/base/middlewares/request-spy/request-spy.middleware.d.ts +11 -1
- package/dist/base/middlewares/request-spy/request-spy.middleware.d.ts.map +1 -1
- package/dist/base/middlewares/request-spy/request-spy.middleware.js +13 -5
- package/dist/base/middlewares/request-spy/request-spy.middleware.js.map +1 -1
- package/dist/components/api-reference/common/keys.d.ts +0 -5
- package/dist/components/api-reference/common/keys.d.ts.map +1 -1
- package/dist/components/api-reference/common/keys.js +1 -7
- package/dist/components/api-reference/common/keys.js.map +1 -1
- package/dist/components/api-reference/common/types.d.ts +1 -2
- package/dist/components/api-reference/common/types.d.ts.map +1 -1
- package/dist/components/api-reference/component.d.ts +0 -4
- package/dist/components/api-reference/component.d.ts.map +1 -1
- package/dist/components/api-reference/component.js +11 -9
- package/dist/components/api-reference/component.js.map +1 -1
- package/dist/components/auth/authenticate/component.d.ts +10 -1
- package/dist/components/auth/authenticate/component.d.ts.map +1 -1
- package/dist/components/auth/authenticate/component.js +79 -25
- package/dist/components/auth/authenticate/component.js.map +1 -1
- package/dist/components/auth/authenticate/controllers/index.d.ts +1 -0
- package/dist/components/auth/authenticate/controllers/index.d.ts.map +1 -1
- package/dist/components/auth/authenticate/controllers/index.js +1 -0
- package/dist/components/auth/authenticate/controllers/index.js.map +1 -1
- package/dist/components/auth/authenticate/controllers/service-certs/controller.d.ts +10 -0
- package/dist/components/auth/authenticate/controllers/service-certs/controller.d.ts.map +1 -0
- package/dist/components/auth/authenticate/controllers/service-certs/controller.js +50 -0
- package/dist/components/auth/authenticate/controllers/service-certs/controller.js.map +1 -0
- package/dist/components/auth/authenticate/controllers/service-certs/definitions.d.ts +73 -0
- package/dist/components/auth/authenticate/controllers/service-certs/definitions.d.ts.map +1 -0
- package/dist/components/auth/authenticate/controllers/service-certs/definitions.js +34 -0
- package/dist/components/auth/authenticate/controllers/service-certs/definitions.js.map +1 -0
- package/dist/components/auth/authenticate/controllers/service-certs/index.d.ts +3 -0
- package/dist/components/auth/authenticate/controllers/service-certs/index.d.ts.map +1 -0
- package/dist/components/auth/authenticate/controllers/service-certs/index.js +19 -0
- package/dist/components/auth/authenticate/controllers/service-certs/index.js.map +1 -0
- package/dist/components/auth/authenticate/services/bearer/abstract.service.d.ts +25 -4
- package/dist/components/auth/authenticate/services/bearer/abstract.service.d.ts.map +1 -1
- package/dist/components/auth/authenticate/services/bearer/abstract.service.js +53 -14
- package/dist/components/auth/authenticate/services/bearer/abstract.service.js.map +1 -1
- package/dist/components/auth/authenticate/services/bearer/jwks/issuer.service.d.ts +18 -1
- package/dist/components/auth/authenticate/services/bearer/jwks/issuer.service.d.ts.map +1 -1
- package/dist/components/auth/authenticate/services/bearer/jwks/issuer.service.js +40 -10
- package/dist/components/auth/authenticate/services/bearer/jwks/issuer.service.js.map +1 -1
- package/dist/components/auth/authenticate/services/bearer/jwks/verifier.service.d.ts +1 -1
- package/dist/components/auth/authenticate/services/bearer/jwks/verifier.service.d.ts.map +1 -1
- package/dist/components/auth/authenticate/services/bearer/jwks/verifier.service.js +4 -5
- package/dist/components/auth/authenticate/services/bearer/jwks/verifier.service.js.map +1 -1
- package/dist/components/auth/authenticate/services/bearer/jws.service.d.ts +2 -1
- package/dist/components/auth/authenticate/services/bearer/jws.service.d.ts.map +1 -1
- package/dist/components/auth/authenticate/services/bearer/jws.service.js +7 -6
- package/dist/components/auth/authenticate/services/bearer/jws.service.js.map +1 -1
- package/dist/components/auth/authenticate/services/index.d.ts +1 -0
- package/dist/components/auth/authenticate/services/index.d.ts.map +1 -1
- package/dist/components/auth/authenticate/services/index.js +1 -0
- package/dist/components/auth/authenticate/services/index.js.map +1 -1
- package/dist/components/auth/authenticate/services/service/index.d.ts +3 -0
- package/dist/components/auth/authenticate/services/service/index.d.ts.map +1 -0
- package/dist/components/auth/authenticate/services/service/index.js +19 -0
- package/dist/components/auth/authenticate/services/service/index.js.map +1 -0
- package/dist/components/auth/authenticate/services/service/signer.service.d.ts +46 -0
- package/dist/components/auth/authenticate/services/service/signer.service.d.ts.map +1 -0
- package/dist/components/auth/authenticate/services/service/signer.service.js +135 -0
- package/dist/components/auth/authenticate/services/service/signer.service.js.map +1 -0
- package/dist/components/auth/authenticate/services/service/verifier.service.d.ts +40 -0
- package/dist/components/auth/authenticate/services/service/verifier.service.d.ts.map +1 -0
- package/dist/components/auth/authenticate/services/service/verifier.service.js +153 -0
- package/dist/components/auth/authenticate/services/service/verifier.service.js.map +1 -0
- package/dist/components/auth/authenticate/strategies/index.d.ts +1 -0
- package/dist/components/auth/authenticate/strategies/index.d.ts.map +1 -1
- package/dist/components/auth/authenticate/strategies/index.js +1 -0
- package/dist/components/auth/authenticate/strategies/index.js.map +1 -1
- package/dist/components/auth/authenticate/strategies/service.strategy.d.ts +19 -0
- package/dist/components/auth/authenticate/strategies/service.strategy.d.ts.map +1 -0
- package/dist/components/auth/authenticate/strategies/service.strategy.js +73 -0
- package/dist/components/auth/authenticate/strategies/service.strategy.js.map +1 -0
- package/dist/components/static-asset/controller/factory.d.ts.map +1 -1
- package/dist/components/static-asset/controller/factory.js +5 -1
- package/dist/components/static-asset/controller/factory.js.map +1 -1
- package/dist/utilities/network.utility.d.ts +9 -0
- package/dist/utilities/network.utility.d.ts.map +1 -1
- package/dist/utilities/network.utility.js +2 -4
- package/dist/utilities/network.utility.js.map +1 -1
- package/package.json +6 -6
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { BaseService } from '@venizia/ignis-kernel';
|
|
2
|
+
import type { IServiceAuthOptions } from '@venizia/ignis-kernel';
|
|
3
|
+
import type { JWK } from 'jose';
|
|
4
|
+
/**
|
|
5
|
+
* Mints the outgoing assertion, and publishes the public half for callees to verify against.
|
|
6
|
+
*
|
|
7
|
+
* Only a service that CALLS OUT needs this. `isEnabled()` is false when the application configured
|
|
8
|
+
* no keys, and the component then mounts no certs route.
|
|
9
|
+
*/
|
|
10
|
+
export declare class ServiceAssertionSignerService extends BaseService {
|
|
11
|
+
private readonly options;
|
|
12
|
+
private readonly requestIdGenerator;
|
|
13
|
+
private privateKey?;
|
|
14
|
+
private jwks?;
|
|
15
|
+
constructor(options: IServiceAuthOptions);
|
|
16
|
+
isEnabled(): boolean;
|
|
17
|
+
/** `<name>_<rest path without its slash>`, so a rotated mount changes the kid rather than colliding. */
|
|
18
|
+
getKeyId(): string;
|
|
19
|
+
/**
|
|
20
|
+
* One assertion, bound to this exact call.
|
|
21
|
+
*
|
|
22
|
+
* `path` must be the percent-ENCODED pathname - `new URL(target).pathname`. The verifier compares
|
|
23
|
+
* against the same shape, and a decoded path here would fail every route carrying a space or a
|
|
24
|
+
* non-ASCII slug.
|
|
25
|
+
*/
|
|
26
|
+
sign(opts: {
|
|
27
|
+
method: string;
|
|
28
|
+
path: string;
|
|
29
|
+
audience: string;
|
|
30
|
+
}): Promise<string>;
|
|
31
|
+
/**
|
|
32
|
+
* The document `/svc-certs` serves, built from the PUBLIC key only.
|
|
33
|
+
*
|
|
34
|
+
* `importSPKI` refuses a private PEM outright, which is what makes the private half unreachable
|
|
35
|
+
* from here by construction rather than by care. The JWK format is deliberately NOT accepted for
|
|
36
|
+
* this path: `importJWK` imports whatever it is handed, and a private JWK marked `"ext": true`
|
|
37
|
+
* round-trips its `d` straight into this document. See `JWKSIssuerTokenService.assertPublicJWK`.
|
|
38
|
+
*/
|
|
39
|
+
getPublicJWKS(): Promise<{
|
|
40
|
+
keys: JWK[];
|
|
41
|
+
}>;
|
|
42
|
+
private resolvePrivateKey;
|
|
43
|
+
private readKey;
|
|
44
|
+
private assertEnabled;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=signer.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signer.service.d.ts","sourceRoot":"","sources":["../../../../../../src/components/auth/authenticate/services/service/signer.service.ts"],"names":[],"mappings":"AACA,OAAO,EAA2B,WAAW,EAAoB,MAAM,uBAAuB,CAAC;AAC/F,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAIjE,OAAO,KAAK,EAAa,GAAG,EAAE,MAAM,MAAM,CAAC;AAI3C;;;;;GAKG;AACH,qBAAa,6BAA8B,SAAQ,WAAW;IAU1D,OAAO,CAAC,QAAQ,CAAC,OAAO;IAT1B,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAEhC;IAEH,OAAO,CAAC,UAAU,CAAC,CAAY;IAC/B,OAAO,CAAC,IAAI,CAAC,CAAkB;gBAIZ,OAAO,EAAE,mBAAmB;IAK/C,SAAS,IAAI,OAAO;IAIpB,wGAAwG;IACxG,QAAQ,IAAI,MAAM;IAKlB;;;;;;OAMG;IACG,IAAI,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IAwBrF;;;;;;;OAOG;IACG,aAAa,IAAI,OAAO,CAAC;QAAE,IAAI,EAAE,GAAG,EAAE,CAAA;KAAE,CAAC;YAyBjC,iBAAiB;YAUjB,OAAO;IAiBrB,OAAO,CAAC,aAAa;CAUtB"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
var ServiceAssertionSignerService_1;
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.ServiceAssertionSignerService = void 0;
|
|
17
|
+
const metadata_1 = require("../../../../../base/metadata");
|
|
18
|
+
const ignis_kernel_1 = require("@venizia/ignis-kernel");
|
|
19
|
+
const core_1 = require("@venizia/ignis-helpers/core");
|
|
20
|
+
const common_1 = require("@venizia/ignis-helpers/common");
|
|
21
|
+
const jose_1 = require("jose");
|
|
22
|
+
const promises_1 = require("node:fs/promises");
|
|
23
|
+
const ignis_kernel_2 = require("@venizia/ignis-kernel");
|
|
24
|
+
/**
|
|
25
|
+
* Mints the outgoing assertion, and publishes the public half for callees to verify against.
|
|
26
|
+
*
|
|
27
|
+
* Only a service that CALLS OUT needs this. `isEnabled()` is false when the application configured
|
|
28
|
+
* no keys, and the component then mounts no certs route.
|
|
29
|
+
*/
|
|
30
|
+
let ServiceAssertionSignerService = ServiceAssertionSignerService_1 = class ServiceAssertionSignerService extends ignis_kernel_1.BaseService {
|
|
31
|
+
constructor(options) {
|
|
32
|
+
super({ scope: ServiceAssertionSignerService_1.name });
|
|
33
|
+
this.options = options;
|
|
34
|
+
this.requestIdGenerator = new core_1.RequestIdGenerator({
|
|
35
|
+
scope: ServiceAssertionSignerService_1.name,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
isEnabled() {
|
|
39
|
+
return Boolean(this.options.keys);
|
|
40
|
+
}
|
|
41
|
+
/** `<name>_<rest path without its slash>`, so a rotated mount changes the kid rather than colliding. */
|
|
42
|
+
getKeyId() {
|
|
43
|
+
const restPath = this.options.rest?.path ?? ignis_kernel_1.ServiceAssertion.DEFAULT_REST_PATH;
|
|
44
|
+
return this.options.keys?.kid ?? `${this.options.name}_${restPath.replace(/^\//, '')}`;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* One assertion, bound to this exact call.
|
|
48
|
+
*
|
|
49
|
+
* `path` must be the percent-ENCODED pathname - `new URL(target).pathname`. The verifier compares
|
|
50
|
+
* against the same shape, and a decoded path here would fail every route carrying a space or a
|
|
51
|
+
* non-ASCII slug.
|
|
52
|
+
*/
|
|
53
|
+
async sign(opts) {
|
|
54
|
+
const { method, path, audience } = opts;
|
|
55
|
+
this.assertEnabled();
|
|
56
|
+
const now = Math.floor(Date.now() / 1000);
|
|
57
|
+
const lifetime = this.options.signLifetimeSeconds ?? ignis_kernel_1.ServiceAssertion.DEFAULT_SIGN_LIFETIME_SECONDS;
|
|
58
|
+
return new jose_1.SignJWT({ htm: method.toUpperCase(), htu: path })
|
|
59
|
+
.setProtectedHeader({
|
|
60
|
+
alg: ignis_kernel_1.ServiceAssertion.ALGORITHM,
|
|
61
|
+
kid: this.getKeyId(),
|
|
62
|
+
typ: ignis_kernel_1.ServiceAssertion.TYP,
|
|
63
|
+
})
|
|
64
|
+
.setIssuer(this.options.name)
|
|
65
|
+
.setSubject(this.options.name)
|
|
66
|
+
.setAudience(audience)
|
|
67
|
+
.setJti(this.requestIdGenerator.nextId())
|
|
68
|
+
.setIssuedAt(now)
|
|
69
|
+
.setExpirationTime(now + lifetime)
|
|
70
|
+
.sign(await this.resolvePrivateKey());
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* The document `/svc-certs` serves, built from the PUBLIC key only.
|
|
74
|
+
*
|
|
75
|
+
* `importSPKI` refuses a private PEM outright, which is what makes the private half unreachable
|
|
76
|
+
* from here by construction rather than by care. The JWK format is deliberately NOT accepted for
|
|
77
|
+
* this path: `importJWK` imports whatever it is handed, and a private JWK marked `"ext": true`
|
|
78
|
+
* round-trips its `d` straight into this document. See `JWKSIssuerTokenService.assertPublicJWK`.
|
|
79
|
+
*/
|
|
80
|
+
async getPublicJWKS() {
|
|
81
|
+
if (this.jwks) {
|
|
82
|
+
return this.jwks;
|
|
83
|
+
}
|
|
84
|
+
this.assertEnabled();
|
|
85
|
+
const raw = await this.readKey({ source: this.options.keys.public });
|
|
86
|
+
const publicKey = await (0, jose_1.importSPKI)(raw, ignis_kernel_1.ServiceAssertion.ALGORITHM);
|
|
87
|
+
const jwk = await (0, jose_1.exportJWK)(publicKey);
|
|
88
|
+
if (jwk.d !== undefined) {
|
|
89
|
+
throw (0, core_1.getError)({
|
|
90
|
+
statusCode: common_1.HTTP.ResultCodes.RS_5.InternalServerError,
|
|
91
|
+
message: `[${ServiceAssertionSignerService_1.name}] The public key material carries a private exponent | this key would be published at the certs endpoint`,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
this.jwks = {
|
|
95
|
+
keys: [{ ...jwk, kid: this.getKeyId(), alg: ignis_kernel_1.ServiceAssertion.ALGORITHM, use: 'sig' }],
|
|
96
|
+
};
|
|
97
|
+
return this.jwks;
|
|
98
|
+
}
|
|
99
|
+
async resolvePrivateKey() {
|
|
100
|
+
if (this.privateKey) {
|
|
101
|
+
return this.privateKey;
|
|
102
|
+
}
|
|
103
|
+
const raw = await this.readKey({ source: this.options.keys.private });
|
|
104
|
+
this.privateKey = await (0, jose_1.importPKCS8)(raw, ignis_kernel_1.ServiceAssertion.ALGORITHM);
|
|
105
|
+
return this.privateKey;
|
|
106
|
+
}
|
|
107
|
+
async readKey(opts) {
|
|
108
|
+
const { format, driver } = this.options.keys;
|
|
109
|
+
if (format !== ignis_kernel_2.JWKSKeyFormats.PEM) {
|
|
110
|
+
throw (0, core_1.getError)({
|
|
111
|
+
statusCode: common_1.HTTP.ResultCodes.RS_5.InternalServerError,
|
|
112
|
+
message: `[${ServiceAssertionSignerService_1.name}] Only the '${ignis_kernel_2.JWKSKeyFormats.PEM}' key format is supported | got: ${format}`,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
if (driver === ignis_kernel_2.JWKSKeyDrivers.FILE) {
|
|
116
|
+
return (0, promises_1.readFile)(opts.source, 'utf-8');
|
|
117
|
+
}
|
|
118
|
+
return opts.source;
|
|
119
|
+
}
|
|
120
|
+
assertEnabled() {
|
|
121
|
+
if (this.options.keys) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
throw (0, core_1.getError)({
|
|
125
|
+
statusCode: common_1.HTTP.ResultCodes.RS_5.InternalServerError,
|
|
126
|
+
message: `[${ServiceAssertionSignerService_1.name}] This service has no signing keys | it can verify assertions but not mint them`,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
exports.ServiceAssertionSignerService = ServiceAssertionSignerService;
|
|
131
|
+
exports.ServiceAssertionSignerService = ServiceAssertionSignerService = ServiceAssertionSignerService_1 = __decorate([
|
|
132
|
+
__param(0, (0, metadata_1.inject)({ key: ignis_kernel_1.AuthenticateBindingKeys.SERVICE_OPTIONS })),
|
|
133
|
+
__metadata("design:paramtypes", [Object])
|
|
134
|
+
], ServiceAssertionSignerService);
|
|
135
|
+
//# sourceMappingURL=signer.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signer.service.js","sourceRoot":"","sources":["../../../../../../src/components/auth/authenticate/services/service/signer.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { BaseService } from '@venizia/ignis-kernel';
|
|
2
|
+
import type { IServiceAuthOptions } from '@venizia/ignis-kernel';
|
|
3
|
+
/**
|
|
4
|
+
* Verifies an incoming service assertion against the CALLER's own JWKS.
|
|
5
|
+
*
|
|
6
|
+
* What the assertion covers, and nothing else: the HTTP method and the percent-encoded path. It does
|
|
7
|
+
* NOT cover the query string, any header, or the body. Tenant selectors in particular are usually a
|
|
8
|
+
* header or a query parameter, so an assertion alone does not authenticate which tenant a call acts
|
|
9
|
+
* on - the caller allowlist and the application's own scoping have to do that. Stated because the
|
|
10
|
+
* gap is invisible from the call site.
|
|
11
|
+
*/
|
|
12
|
+
export declare class ServiceAssertionVerifierService extends BaseService {
|
|
13
|
+
private readonly options;
|
|
14
|
+
/** One remote key set per caller, kept because `createRemoteJWKSet` owns its own cache. */
|
|
15
|
+
private readonly keySets;
|
|
16
|
+
/** Per-caller accepted age, resolved once alongside its key set. */
|
|
17
|
+
private readonly acceptMaxAges;
|
|
18
|
+
constructor(options: IServiceAuthOptions);
|
|
19
|
+
/**
|
|
20
|
+
* @param path The percent-ENCODED pathname, from `new URL(context.req.url).pathname`. Never
|
|
21
|
+
* `context.req.path` - Hono decodes that one, and the caller signed the encoded form.
|
|
22
|
+
*/
|
|
23
|
+
verify(opts: {
|
|
24
|
+
token: string;
|
|
25
|
+
method: string;
|
|
26
|
+
path: string;
|
|
27
|
+
}): Promise<{
|
|
28
|
+
issuer: string;
|
|
29
|
+
}>;
|
|
30
|
+
/**
|
|
31
|
+
* Reads `iss` WITHOUT verifying, only to choose which key set checks the signature. The same value
|
|
32
|
+
* is then enforced as the expected issuer, so a forged `iss` buys nothing beyond selecting the key
|
|
33
|
+
* set that will reject it.
|
|
34
|
+
*/
|
|
35
|
+
private readIssuer;
|
|
36
|
+
private resolveKeySet;
|
|
37
|
+
private runVerify;
|
|
38
|
+
private assertRequestBinding;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=verifier.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verifier.service.d.ts","sourceRoot":"","sources":["../../../../../../src/components/auth/authenticate/services/service/verifier.service.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,WAAW,EAEZ,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAA2B,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAM1F;;;;;;;;GAQG;AACH,qBAAa,+BAAgC,SAAQ,WAAW;IAS5D,OAAO,CAAC,QAAQ,CAAC,OAAO;IAR1B,2FAA2F;IAC3F,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoC;IAE5D,oEAAoE;IACpE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA6B;gBAIxC,OAAO,EAAE,mBAAmB;IAK/C;;;OAGG;IACG,MAAM,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAYhG;;;;OAIG;IACH,OAAO,CAAC,UAAU;IAgBlB,OAAO,CAAC,aAAa;YAyCP,SAAS;IA+CvB,OAAO,CAAC,oBAAoB;CAoB7B"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
var ServiceAssertionVerifierService_1;
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.ServiceAssertionVerifierService = void 0;
|
|
17
|
+
const metadata_1 = require("../../../../../base/metadata");
|
|
18
|
+
const ignis_kernel_1 = require("@venizia/ignis-kernel");
|
|
19
|
+
const core_1 = require("@venizia/ignis-helpers/core");
|
|
20
|
+
const jose_1 = require("jose");
|
|
21
|
+
/**
|
|
22
|
+
* Verifies an incoming service assertion against the CALLER's own JWKS.
|
|
23
|
+
*
|
|
24
|
+
* What the assertion covers, and nothing else: the HTTP method and the percent-encoded path. It does
|
|
25
|
+
* NOT cover the query string, any header, or the body. Tenant selectors in particular are usually a
|
|
26
|
+
* header or a query parameter, so an assertion alone does not authenticate which tenant a call acts
|
|
27
|
+
* on - the caller allowlist and the application's own scoping have to do that. Stated because the
|
|
28
|
+
* gap is invisible from the call site.
|
|
29
|
+
*/
|
|
30
|
+
let ServiceAssertionVerifierService = ServiceAssertionVerifierService_1 = class ServiceAssertionVerifierService extends ignis_kernel_1.BaseService {
|
|
31
|
+
constructor(options) {
|
|
32
|
+
super({ scope: ServiceAssertionVerifierService_1.name });
|
|
33
|
+
this.options = options;
|
|
34
|
+
/** One remote key set per caller, kept because `createRemoteJWKSet` owns its own cache. */
|
|
35
|
+
this.keySets = new Map();
|
|
36
|
+
/** Per-caller accepted age, resolved once alongside its key set. */
|
|
37
|
+
this.acceptMaxAges = new Map();
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* @param path The percent-ENCODED pathname, from `new URL(context.req.url).pathname`. Never
|
|
41
|
+
* `context.req.path` - Hono decodes that one, and the caller signed the encoded form.
|
|
42
|
+
*/
|
|
43
|
+
async verify(opts) {
|
|
44
|
+
const { token, method, path } = opts;
|
|
45
|
+
const issuer = this.readIssuer({ token });
|
|
46
|
+
const keySet = this.resolveKeySet({ issuer });
|
|
47
|
+
const payload = await this.runVerify({ token, issuer, keySet });
|
|
48
|
+
this.assertRequestBinding({ payload, method, path });
|
|
49
|
+
return { issuer };
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Reads `iss` WITHOUT verifying, only to choose which key set checks the signature. The same value
|
|
53
|
+
* is then enforced as the expected issuer, so a forged `iss` buys nothing beyond selecting the key
|
|
54
|
+
* set that will reject it.
|
|
55
|
+
*/
|
|
56
|
+
readIssuer(opts) {
|
|
57
|
+
try {
|
|
58
|
+
const { iss } = (0, jose_1.decodeJwt)(opts.token);
|
|
59
|
+
// Typed, not merely present: a numeric `iss` would index the caller map by coercion.
|
|
60
|
+
if (typeof iss !== 'string' || iss.length === 0) {
|
|
61
|
+
throw new Error('missing or non-string iss');
|
|
62
|
+
}
|
|
63
|
+
return iss;
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
this.logger.for(this.readIssuer.name).warn('Undecodable assertion | error: %s', error);
|
|
67
|
+
throw (0, core_1.getError)({ error: ignis_kernel_1.AuthenticationErrors.ASSERTION_INVALID });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
resolveKeySet(opts) {
|
|
71
|
+
const { issuer } = opts;
|
|
72
|
+
const cached = this.keySets.get(issuer);
|
|
73
|
+
if (cached) {
|
|
74
|
+
return cached;
|
|
75
|
+
}
|
|
76
|
+
const callers = this.options.callers ?? {};
|
|
77
|
+
// `Object.hasOwn`, never a truthiness check on the index: the map arrives as a plain object, so
|
|
78
|
+
// `iss: 'constructor'` would otherwise return `Function` - truthy - walk past this guard, and
|
|
79
|
+
// reach `new URL(<function>)` as an uncaught TypeError.
|
|
80
|
+
if (!Object.hasOwn(callers, issuer)) {
|
|
81
|
+
this.logger
|
|
82
|
+
.for(this.resolveKeySet.name)
|
|
83
|
+
.warn('Caller not on the allowlist | iss: %s', issuer);
|
|
84
|
+
throw (0, core_1.getError)({ error: ignis_kernel_1.AuthenticationErrors.CALLER_NOT_ALLOWED });
|
|
85
|
+
}
|
|
86
|
+
const entry = callers[issuer];
|
|
87
|
+
const jwksUrl = typeof entry === 'string' ? entry : entry.jwksUrl;
|
|
88
|
+
this.acceptMaxAges.set(issuer, ignis_kernel_1.ServiceAssertion.resolveAcceptMaxAge({
|
|
89
|
+
callerOverride: typeof entry === 'string' ? undefined : entry.acceptMaxAgeSeconds,
|
|
90
|
+
serviceDefault: this.options.acceptMaxAgeSeconds,
|
|
91
|
+
}));
|
|
92
|
+
const keySet = (0, jose_1.createRemoteJWKSet)(new URL(jwksUrl), {
|
|
93
|
+
cacheMaxAge: this.options.jwks?.cacheMaxAgeMs ?? ignis_kernel_1.ServiceAssertion.DEFAULT_JWKS_CACHE_MAX_AGE_MS,
|
|
94
|
+
cooldownDuration: this.options.jwks?.cooldownMs ?? ignis_kernel_1.ServiceAssertion.DEFAULT_JWKS_COOLDOWN_MS,
|
|
95
|
+
});
|
|
96
|
+
this.keySets.set(issuer, keySet);
|
|
97
|
+
return keySet;
|
|
98
|
+
}
|
|
99
|
+
async runVerify(opts) {
|
|
100
|
+
const { token, issuer, keySet } = opts;
|
|
101
|
+
// Resolved when the key set was, so a per-caller override is already folded in.
|
|
102
|
+
const maxTokenAge = this.acceptMaxAges.get(issuer) ??
|
|
103
|
+
ignis_kernel_1.ServiceAssertion.resolveAcceptMaxAge({
|
|
104
|
+
serviceDefault: this.options.acceptMaxAgeSeconds,
|
|
105
|
+
});
|
|
106
|
+
const clockTolerance = this.options.clockToleranceSeconds ?? ignis_kernel_1.ServiceAssertion.DEFAULT_CLOCK_TOLERANCE_SECONDS;
|
|
107
|
+
try {
|
|
108
|
+
const { payload } = await (0, jose_1.jwtVerify)(token, keySet, {
|
|
109
|
+
algorithms: [ignis_kernel_1.ServiceAssertion.ALGORITHM],
|
|
110
|
+
typ: ignis_kernel_1.ServiceAssertion.TYP,
|
|
111
|
+
issuer,
|
|
112
|
+
audience: this.options.name,
|
|
113
|
+
// NOT redundant with `exp`, though it looks it. An honest caller stamps `exp = iat +
|
|
114
|
+
// lifetime`, so the two are numerically tied and jose reaches `exp` first - measured.
|
|
115
|
+
// `maxTokenAge` is the only bound that still holds when the CALLER is the attacker and picks
|
|
116
|
+
// its own `exp`, which is precisely the case it exists for. Do not delete it as dead config.
|
|
117
|
+
//
|
|
118
|
+
// And it is the CALLEE's number, never one the caller may request: a caller that could ask
|
|
119
|
+
// for a longer window would be choosing its own replay window, which is the whole thing this
|
|
120
|
+
// is here to deny.
|
|
121
|
+
maxTokenAge,
|
|
122
|
+
clockTolerance,
|
|
123
|
+
requiredClaims: ['htm', 'htu', 'jti'],
|
|
124
|
+
});
|
|
125
|
+
return payload;
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
// One code out, the reason on `cause`: a wrong audience, clock skew and a rotated key are
|
|
129
|
+
// otherwise the same string to whoever is reading the log at 2am.
|
|
130
|
+
const reason = error?.cause ?? error;
|
|
131
|
+
this.logger.for(this.runVerify.name).warn('Rejected | iss: %s | reason: %s', issuer, reason);
|
|
132
|
+
throw (0, core_1.getError)({ error: ignis_kernel_1.AuthenticationErrors.ASSERTION_INVALID, cause: error });
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
assertRequestBinding(opts) {
|
|
136
|
+
const { payload, method, path } = opts;
|
|
137
|
+
const signed = `${payload.htm} ${payload.htu}`;
|
|
138
|
+
const actual = `${method.toUpperCase()} ${path}`;
|
|
139
|
+
if (signed === actual) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
this.logger
|
|
143
|
+
.for(this.assertRequestBinding.name)
|
|
144
|
+
.warn('Request binding mismatch | signed: %s | actual: %s', signed, actual);
|
|
145
|
+
throw (0, core_1.getError)({ error: ignis_kernel_1.AuthenticationErrors.REQUEST_BINDING_MISMATCH });
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
exports.ServiceAssertionVerifierService = ServiceAssertionVerifierService;
|
|
149
|
+
exports.ServiceAssertionVerifierService = ServiceAssertionVerifierService = ServiceAssertionVerifierService_1 = __decorate([
|
|
150
|
+
__param(0, (0, metadata_1.inject)({ key: ignis_kernel_1.AuthenticateBindingKeys.SERVICE_OPTIONS })),
|
|
151
|
+
__metadata("design:paramtypes", [Object])
|
|
152
|
+
], ServiceAssertionVerifierService);
|
|
153
|
+
//# sourceMappingURL=verifier.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verifier.service.js","sourceRoot":"","sources":["../../../../../../src/components/auth/authenticate/services/service/verifier.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/components/auth/authenticate/strategies/index.ts"],"names":[],"mappings":"AACA,cAAc,uBAAuB,CAAC;AAEtC,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/components/auth/authenticate/strategies/index.ts"],"names":[],"mappings":"AACA,cAAc,uBAAuB,CAAC;AAEtC,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC"}
|
|
@@ -19,4 +19,5 @@ __exportStar(require("@venizia/ignis-kernel"), exports);
|
|
|
19
19
|
__exportStar(require("./basic.strategy"), exports);
|
|
20
20
|
__exportStar(require("./jws.strategy"), exports);
|
|
21
21
|
__exportStar(require("./jwks.strategy"), exports);
|
|
22
|
+
__exportStar(require("./service.strategy"), exports);
|
|
22
23
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/components/auth/authenticate/strategies/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,iGAAiG;AACjG,wDAAsC;AAEtC,mDAAiC;AACjC,iDAA+B;AAC/B,kDAAgC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/components/auth/authenticate/strategies/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,iGAAiG;AACjG,wDAAsC;AAEtC,mDAAiC;AACjC,iDAA+B;AAC/B,kDAAgC;AAChC,qDAAmC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { BaseHelper } from '@venizia/ignis-helpers/core';
|
|
2
|
+
import type { Env } from 'hono';
|
|
3
|
+
import type { IAuthUser, IAuthenticationStrategy, IServiceAuthOptions, TContext } from '@venizia/ignis-kernel';
|
|
4
|
+
import { ServiceAssertionVerifierService } from '../services/service/verifier.service';
|
|
5
|
+
/**
|
|
6
|
+
* Proves the CALLING SERVICE, from a per-request Ed25519 assertion verified against that caller's
|
|
7
|
+
* own JWKS. No identity round trip and no shared password.
|
|
8
|
+
*
|
|
9
|
+
* It proves which service called, never which user. `resolvePrincipal` is where the application
|
|
10
|
+
* turns a caller name into one of its own principals.
|
|
11
|
+
*/
|
|
12
|
+
export declare class ServiceAuthenticationStrategy<E extends Env = Env> extends BaseHelper implements IAuthenticationStrategy<E> {
|
|
13
|
+
private readonly verifier;
|
|
14
|
+
private readonly options;
|
|
15
|
+
name: string;
|
|
16
|
+
constructor(verifier: ServiceAssertionVerifierService, options: IServiceAuthOptions<E>);
|
|
17
|
+
authenticate(context: TContext<E, string>): Promise<IAuthUser>;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=service.strategy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.strategy.d.ts","sourceRoot":"","sources":["../../../../../src/components/auth/authenticate/strategies/service.strategy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAY,MAAM,6BAA6B,CAAC;AAEnE,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,MAAM,CAAC;AAShC,OAAO,KAAK,EACV,SAAS,EACT,uBAAuB,EACvB,mBAAmB,EACnB,QAAQ,EACT,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,+BAA+B,EAAE,MAAM,sCAAsC,CAAC;AAEvF;;;;;;GAMG;AACH,qBAAa,6BAA6B,CAAC,CAAC,SAAS,GAAG,GAAG,GAAG,CAC5D,SAAQ,UACR,YAAW,uBAAuB,CAAC,CAAC,CAAC;IAWnC,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAEzB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAX1B,IAAI,SAAmC;gBASpB,QAAQ,EAAE,+BAA+B,EAEzC,OAAO,EAAE,mBAAmB,CAAC,CAAC,CAAC;IAK5C,YAAY,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC;CA8BrE"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
var ServiceAuthenticationStrategy_1;
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.ServiceAuthenticationStrategy = void 0;
|
|
17
|
+
const core_1 = require("@venizia/ignis-helpers/core");
|
|
18
|
+
const ignis_inversion_1 = require("@venizia/ignis-inversion");
|
|
19
|
+
const ignis_kernel_1 = require("@venizia/ignis-kernel");
|
|
20
|
+
const verifier_service_1 = require("../services/service/verifier.service");
|
|
21
|
+
/**
|
|
22
|
+
* Proves the CALLING SERVICE, from a per-request Ed25519 assertion verified against that caller's
|
|
23
|
+
* own JWKS. No identity round trip and no shared password.
|
|
24
|
+
*
|
|
25
|
+
* It proves which service called, never which user. `resolvePrincipal` is where the application
|
|
26
|
+
* turns a caller name into one of its own principals.
|
|
27
|
+
*/
|
|
28
|
+
let ServiceAuthenticationStrategy = ServiceAuthenticationStrategy_1 = class ServiceAuthenticationStrategy extends core_1.BaseHelper {
|
|
29
|
+
constructor(verifier, options) {
|
|
30
|
+
super({ scope: ServiceAuthenticationStrategy_1.name });
|
|
31
|
+
this.verifier = verifier;
|
|
32
|
+
this.options = options;
|
|
33
|
+
this.name = ignis_kernel_1.Authentication.STRATEGY_SERVICE;
|
|
34
|
+
}
|
|
35
|
+
async authenticate(context) {
|
|
36
|
+
const token = context.req.header(ignis_kernel_1.ServiceAssertion.HEADER);
|
|
37
|
+
if (!token) {
|
|
38
|
+
throw (0, core_1.getError)({ error: ignis_kernel_1.AuthenticationErrors.ASSERTION_MISSING });
|
|
39
|
+
}
|
|
40
|
+
// The raw url, NOT `context.req.path`: Hono hands that back percent-DECODED, so a route carrying
|
|
41
|
+
// a space or a non-ASCII slug would never match the encoded path the caller signed.
|
|
42
|
+
const { issuer } = await this.verifier.verify({
|
|
43
|
+
token,
|
|
44
|
+
method: context.req.method,
|
|
45
|
+
path: new URL(context.req.url).pathname,
|
|
46
|
+
});
|
|
47
|
+
const principal = await this.options.resolvePrincipal({ issuer, context });
|
|
48
|
+
// Validated rather than trusted. `executeAnyMode` calls `setCurrentUser` unconditionally, so an
|
|
49
|
+
// application returning a principal without a `userId` would authenticate the request and then
|
|
50
|
+
// fail at the first write, far from here. `executeAllMode` already refuses it; this closes the
|
|
51
|
+
// gap between the two modes.
|
|
52
|
+
if (!principal?.userId) {
|
|
53
|
+
this.logger
|
|
54
|
+
.for(this.authenticate.name)
|
|
55
|
+
.warn('resolvePrincipal returned no usable principal | iss: %s', issuer);
|
|
56
|
+
throw (0, core_1.getError)({ error: ignis_kernel_1.AuthenticationErrors.PRINCIPAL_UNRESOLVED });
|
|
57
|
+
}
|
|
58
|
+
// Which service called is audit-relevant even when the principal is shared across callers.
|
|
59
|
+
return { ...principal, callerService: issuer };
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
exports.ServiceAuthenticationStrategy = ServiceAuthenticationStrategy;
|
|
63
|
+
exports.ServiceAuthenticationStrategy = ServiceAuthenticationStrategy = ServiceAuthenticationStrategy_1 = __decorate([
|
|
64
|
+
__param(0, (0, ignis_kernel_1.inject)({
|
|
65
|
+
key: ignis_inversion_1.BindingKeys.build({
|
|
66
|
+
namespace: ignis_kernel_1.BindingNamespaces.SERVICE,
|
|
67
|
+
key: verifier_service_1.ServiceAssertionVerifierService.name,
|
|
68
|
+
}),
|
|
69
|
+
})),
|
|
70
|
+
__param(1, (0, ignis_kernel_1.inject)({ key: ignis_kernel_1.AuthenticateBindingKeys.SERVICE_OPTIONS })),
|
|
71
|
+
__metadata("design:paramtypes", [verifier_service_1.ServiceAssertionVerifierService, Object])
|
|
72
|
+
], ServiceAuthenticationStrategy);
|
|
73
|
+
//# sourceMappingURL=service.strategy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.strategy.js","sourceRoot":"","sources":["../../../../../src/components/auth/authenticate/strategies/service.strategy.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,sDAAmE;AACnE,8DAAuD;AAEvD,wDAO+B;AAO/B,2EAAuF;AAEvF;;;;;;GAMG;AACH,IAAa,6BAA6B,qCAA1C,MAAa,6BACX,SAAQ,iBAAU;IAKlB,YAOE,QAA0D,EAE1D,OAAgD;QAEhD,KAAK,CAAC,EAAE,KAAK,EAAE,+BAA6B,CAAC,IAAI,EAAE,CAAC,CAAC;QAJpC,aAAQ,GAAR,QAAQ,CAAiC;QAEzC,YAAO,GAAP,OAAO,CAAwB;QAXlD,SAAI,GAAG,6BAAc,CAAC,gBAAgB,CAAC;IAcvC,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,OAA4B;QAC7C,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,+BAAgB,CAAC,MAAM,CAAC,CAAC;QAC1D,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAA,eAAQ,EAAC,EAAE,KAAK,EAAE,mCAAoB,CAAC,iBAAiB,EAAE,CAAC,CAAC;QACpE,CAAC;QAED,iGAAiG;QACjG,oFAAoF;QACpF,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;YAC5C,KAAK;YACL,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,MAAM;YAC1B,IAAI,EAAE,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ;SACxC,CAAC,CAAC;QAEH,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,gBAAgB,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;QAE3E,gGAAgG;QAChG,+FAA+F;QAC/F,+FAA+F;QAC/F,6BAA6B;QAC7B,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,CAAC;YACvB,IAAI,CAAC,MAAM;iBACR,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC;iBAC3B,IAAI,CAAC,yDAAyD,EAAE,MAAM,CAAC,CAAC;YAC3E,MAAM,IAAA,eAAQ,EAAC,EAAE,KAAK,EAAE,mCAAoB,CAAC,oBAAoB,EAAE,CAAC,CAAC;QACvE,CAAC;QAED,2FAA2F;QAC3F,OAAO,EAAE,GAAG,SAAS,EAAE,aAAa,EAAE,MAAM,EAAE,CAAC;IACjD,CAAC;CACF,CAAA;AAlDY,sEAA6B;wCAA7B,6BAA6B;IAOrC,WAAA,IAAA,qBAAM,EAAC;QACN,GAAG,EAAE,6BAAW,CAAC,KAAK,CAAC;YACrB,SAAS,EAAE,gCAAiB,CAAC,OAAO;YACpC,GAAG,EAAE,kDAA+B,CAAC,IAAI;SAC1C,CAAC;KACH,CAAC,CAAA;IAED,WAAA,IAAA,qBAAM,EAAC,EAAE,GAAG,EAAE,sCAAuB,CAAC,eAAe,EAAE,CAAC,CAAA;qCAD9B,kDAA+B;GAbjD,6BAA6B,CAkDzC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../../../src/components/static-asset/controller/factory.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAY,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAQ,cAAc,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,EAGL,cAAc,EAIf,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,GAAG,EAAE,MAAM,MAAM,CAAC;AAE3B,OAAO,EAIL,eAAe,EAEf,wBAAwB,EACxB,6BAA6B,EAC7B,uBAAuB,EAGxB,MAAM,WAAW,CAAC;AAGnB,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,6BAA6B,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;IAChE,OAAO,EAAE,uBAAuB,CAAC;IACjC,MAAM,EAAE,cAAc,CAAC;IACvB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,OAAO,CAAC,EAAE,wBAAwB,CAAC;CACpC;AAuBD,qBAAa,sBAAuB,SAAQ,UAAU;;IAKpD,MAAM,CAAC,qBAAqB,CAAC,IAAI,EAAE,uBAAuB;;YAkBtD,6FAA6F;2CAC9D;gBAAE,KAAK,EAAE,MAAM,EAAE,CAAA;aAAE,GAAG,IAAI;uBAYrC,cAAc,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAxDiC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../../../src/components/static-asset/controller/factory.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAY,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAQ,cAAc,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,EAGL,cAAc,EAIf,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,GAAG,EAAE,MAAM,MAAM,CAAC;AAE3B,OAAO,EAIL,eAAe,EAEf,wBAAwB,EACxB,6BAA6B,EAC7B,uBAAuB,EAGxB,MAAM,WAAW,CAAC;AAGnB,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,6BAA6B,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;IAChE,OAAO,EAAE,uBAAuB,CAAC;IACjC,MAAM,EAAE,cAAc,CAAC;IACvB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,OAAO,CAAC,EAAE,wBAAwB,CAAC;CACpC;AAuBD,qBAAa,sBAAuB,SAAQ,UAAU;;IAKpD,MAAM,CAAC,qBAAqB,CAAC,IAAI,EAAE,uBAAuB;;YAkBtD,6FAA6F;2CAC9D;gBAAE,KAAK,EAAE,MAAM,EAAE,CAAA;aAAE,GAAG,IAAI;uBAYrC,cAAc,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAxDiC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAif/E"}
|
|
@@ -270,7 +270,11 @@ class AssetControllerFactory extends core_1.BaseHelper {
|
|
|
270
270
|
results.push({
|
|
271
271
|
...uploadResult,
|
|
272
272
|
metaLink: null,
|
|
273
|
-
|
|
273
|
+
// A CODE, never the driver text. This handler returns 200, so it bypasses the
|
|
274
|
+
// error middleware and `database.handler` - the two places that strip
|
|
275
|
+
// `detail`/`table`/`constraint` - and shipped raw constraint names to the client.
|
|
276
|
+
// The real error is already logged in full immediately above.
|
|
277
|
+
metaLinkError: 'META_LINK_CREATE_FAILED',
|
|
274
278
|
});
|
|
275
279
|
}
|
|
276
280
|
}
|