@nodii/grpc-interceptors 0.5.3 → 0.6.1
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/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +30 -1
- package/dist/factory.js.map +1 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/interceptors/enrich-auth.d.ts.map +1 -1
- package/dist/interceptors/enrich-auth.js +13 -2
- package/dist/interceptors/enrich-auth.js.map +1 -1
- package/dist/interceptors/mfa-required.d.ts +19 -0
- package/dist/interceptors/mfa-required.d.ts.map +1 -0
- package/dist/interceptors/mfa-required.js +100 -0
- package/dist/interceptors/mfa-required.js.map +1 -0
- package/dist/interceptors/saga-envelope.d.ts +13 -0
- package/dist/interceptors/saga-envelope.d.ts.map +1 -0
- package/dist/interceptors/saga-envelope.js +76 -0
- package/dist/interceptors/saga-envelope.js.map +1 -0
- package/dist/interceptors/saga-idempotency.d.ts +29 -0
- package/dist/interceptors/saga-idempotency.d.ts.map +1 -0
- package/dist/interceptors/saga-idempotency.js +184 -0
- package/dist/interceptors/saga-idempotency.js.map +1 -0
- package/dist/saga-envelope.d.ts +70 -0
- package/dist/saga-envelope.d.ts.map +1 -0
- package/dist/saga-envelope.js +131 -0
- package/dist/saga-envelope.js.map +1 -0
- package/dist/types.d.ts +57 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +9 -6
package/dist/factory.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EACV,yBAAyB,EAGzB,gBAAgB,EACjB,MAAM,SAAS,CAAC;AAUjB;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,yBAAyB,GAChC,gBAAgB,CA0HlB"}
|
package/dist/factory.js
CHANGED
|
@@ -9,6 +9,9 @@
|
|
|
9
9
|
// this lib) before passing it to the composed factory.
|
|
10
10
|
import { idempotencyGuard } from "@nodii/idempotency";
|
|
11
11
|
import { sagaContext } from "@nodii/saga";
|
|
12
|
+
import { withMfaRequiredUnary } from "./interceptors/mfa-required";
|
|
13
|
+
import { withSagaEnvelopeUnary } from "./interceptors/saga-envelope";
|
|
14
|
+
import { withSagaIdempotencyUnary } from "./interceptors/saga-idempotency";
|
|
12
15
|
import { signalBinder } from "./interceptors/signal-binder";
|
|
13
16
|
import { cancellationGuard, deadlineGuard, enrichAuthContext, errorMap, STANDARD_ERROR_CATALOG, tenantContext, withAuditUnary, withLoggingUnary, auditContext, } from "./interceptors-exports";
|
|
14
17
|
import { GrpcStatusError } from "./types";
|
|
@@ -58,13 +61,36 @@ export function createStandardServerStack(config) {
|
|
|
58
61
|
withAuditUnary(config.audit ?? {}),
|
|
59
62
|
passthrough(), // withAuthUnary slot — service-wired
|
|
60
63
|
enrichAuthContext(config.enrich ?? {}),
|
|
64
|
+
// OPT-IN saga-envelope leg (D423) — parse the 7 `x-saga-*` headers into
|
|
65
|
+
// `aug.sagaEnvelope` BEFORE the saga-context + idempotency cluster so the
|
|
66
|
+
// saga-idempotency leg can read the full envelope. Omitted = passthrough.
|
|
67
|
+
config.sagaEnvelope ? withSagaEnvelopeUnary() : passthrough(),
|
|
61
68
|
(methodName, handler) => {
|
|
62
69
|
const inner = builtSagaContext(methodName, handler);
|
|
63
70
|
return async (call) => inner(call);
|
|
64
71
|
},
|
|
65
72
|
signalBinder(),
|
|
73
|
+
// OPT-IN saga-aware idempotency leg (D423) — engages ONLY for calls
|
|
74
|
+
// carrying a saga envelope; keys on `(tenant_id, method, saga_id,
|
|
75
|
+
// step_id)` over the service idempotency store + rejects a caller key.
|
|
76
|
+
// Ordered BEFORE the generic idempotency-guard so a saga call is claimed
|
|
77
|
+
// under the envelope key (the two are mutually exclusive: saga-idem only
|
|
78
|
+
// runs when an envelope is present, the generic guard's caller-header path
|
|
79
|
+
// is for non-saga mutators).
|
|
80
|
+
config.sagaIdempotency
|
|
81
|
+
? withSagaIdempotencyUnary(typeof config.sagaIdempotency === "object"
|
|
82
|
+
? config.sagaIdempotency
|
|
83
|
+
: {})
|
|
84
|
+
: passthrough(),
|
|
66
85
|
(methodName, handler) => {
|
|
67
86
|
return async (call) => {
|
|
87
|
+
// A saga call (envelope present) is claimed by the saga-idempotency
|
|
88
|
+
// leg (outer) under the canonical `(tenant, method, saga_id, step_id)`
|
|
89
|
+
// key — the generic caller-header guard MUST NOT also fire (it would
|
|
90
|
+
// wrongly reject the missing caller `x-nodii-idempotency-key`).
|
|
91
|
+
if (call.sagaEnvelope) {
|
|
92
|
+
return handler(call);
|
|
93
|
+
}
|
|
68
94
|
call.__methodName =
|
|
69
95
|
methodName;
|
|
70
96
|
const guarded = builtIdemGuard(handler);
|
|
@@ -83,7 +109,10 @@ export function createStandardServerStack(config) {
|
|
|
83
109
|
}),
|
|
84
110
|
]
|
|
85
111
|
: []),
|
|
86
|
-
|
|
112
|
+
// OPT-IN MFA leg (D423) — step-up MFA enforcement at the locked mfa slot.
|
|
113
|
+
// Runs AFTER auth/enrich/tenant/audit-context (needs the verified token)
|
|
114
|
+
// but BEFORE deadline/cancellation/error-map. Omitted = passthrough.
|
|
115
|
+
config.mfa ? withMfaRequiredUnary(config.mfa) : passthrough(),
|
|
87
116
|
deadlineGuard(config.deadline ?? {}),
|
|
88
117
|
cancellationGuard(config.cancellation ?? {}),
|
|
89
118
|
errorMap({
|
package/dist/factory.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factory.js","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"factory.js","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -8,6 +8,13 @@ export { auditContext } from "./interceptors/audit-context";
|
|
|
8
8
|
export { deadlineGuard } from "./interceptors/deadline-guard";
|
|
9
9
|
export { cancellationGuard } from "./interceptors/cancellation-guard";
|
|
10
10
|
export { errorMap, STANDARD_ERROR_CATALOG, } from "./interceptors/error-map";
|
|
11
|
+
export { withMfaRequiredUnary, createMfaPolicy, MFA_FRESHNESS_SECONDS, } from "./interceptors/mfa-required";
|
|
12
|
+
export { withSagaEnvelopeUnary } from "./interceptors/saga-envelope";
|
|
13
|
+
export type { CallWithSagaEnvelope } from "./interceptors/saga-envelope";
|
|
14
|
+
export { withSagaIdempotencyUnary, computeSagaIdempotencyDigest, } from "./interceptors/saga-idempotency";
|
|
15
|
+
export type { SagaIdempotencyConfig } from "./interceptors/saga-idempotency";
|
|
16
|
+
export { parseSagaEnvelope, SAGA_ENVELOPE_HEADERS, } from "./saga-envelope";
|
|
17
|
+
export type { SagaEnvelope, SagaEnvelopeHeaders, SagaEnvelopeParseError, } from "./saga-envelope";
|
|
11
18
|
export { createStandardServerStack } from "./factory";
|
|
12
19
|
export { composeUnaryInterceptors } from "./compose";
|
|
13
20
|
export { getAuditFramingTableName, normaliseServiceName, makeAuditFramingTableDDL, auditFramingIndexDDL, AUDIT_ACTOR_KIND_ENUM_DDL, AUDIT_OUTCOME_ENUM_DDL, PostgresFramingWriter, RecordingFramingWriter, } from "./framing";
|
|
@@ -17,6 +24,6 @@ export type { ConfigureGrpcInterceptorsOptions, ConfiguredGrpcInterceptors, Reso
|
|
|
17
24
|
export { _resetTelemetryShimForTests, _setTelemetryShimForTests, } from "./telemetry-shim";
|
|
18
25
|
export * from "./reexports";
|
|
19
26
|
export type { SagaHeaderInjector } from "./interceptors/saga-headers";
|
|
20
|
-
export type { AuditConfig, AuditContextConfig, AuditEmitter, AuthOnCall, CancellationConfig, DbConnection, DeadlineConfig, EnrichAuthConfig, ErrorMapEntry, GrpcStatusCode, InterceptorLogger, LoggingConfig, StandardServerStackConfig, TelemetryShim, TenantContextConfig, UnaryCall, UnaryHandler, UnaryInterceptor, } from "./types";
|
|
27
|
+
export type { AuditConfig, AuditContextConfig, AuditEmitter, AuthOnCall, CancellationConfig, DbConnection, DeadlineConfig, EnrichAuthConfig, ErrorMapEntry, GrpcStatusCode, InterceptorLogger, LoggingConfig, MfaPolicy, MfaRequiredConfig, SagaIdempotencyLegConfig, StandardServerStackConfig, TelemetryShim, TenantContextConfig, UnaryCall, UnaryHandler, UnaryInterceptor, } from "./types";
|
|
21
28
|
export { GrpcStatusError, NotImplementedError, RpcCancelledError, } from "./types";
|
|
22
29
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,QAAQ,sBAAsB,CAAC;AAC5C,eAAO,MAAM,OAAO,UAAU,CAAC;AAG/B,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,EACL,QAAQ,EACR,sBAAsB,GACvB,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,QAAQ,sBAAsB,CAAC;AAC5C,eAAO,MAAM,OAAO,UAAU,CAAC;AAG/B,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,EACL,QAAQ,EACR,sBAAsB,GACvB,MAAM,0BAA0B,CAAC;AAKlC,OAAO,EACL,oBAAoB,EACpB,eAAe,EACf,qBAAqB,GACtB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,YAAY,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACzE,OAAO,EACL,wBAAwB,EACxB,4BAA4B,GAC7B,MAAM,iCAAiC,CAAC;AACzC,YAAY,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AAC7E,OAAO,EACL,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACV,YAAY,EACZ,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC;AAGrD,OAAO,EACL,wBAAwB,EACxB,oBAAoB,EACpB,wBAAwB,EACxB,oBAAoB,EACpB,yBAAyB,EACzB,sBAAsB,EACtB,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,WAAW,CAAC;AACnB,YAAY,EACV,UAAU,EACV,aAAa,EACb,kBAAkB,GACnB,MAAM,WAAW,CAAC;AAGnB,OAAO,EACL,yBAAyB,EACzB,mBAAmB,EACnB,uBAAuB,GACxB,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,gCAAgC,EAChC,0BAA0B,EAC1B,8BAA8B,GAC/B,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,2BAA2B,EAC3B,yBAAyB,GAC1B,MAAM,kBAAkB,CAAC;AAI1B,cAAc,aAAa,CAAC;AAC5B,YAAY,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAGtE,YAAY,EACV,WAAW,EACX,kBAAkB,EAClB,YAAY,EACZ,UAAU,EACV,kBAAkB,EAClB,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,aAAa,EACb,cAAc,EACd,iBAAiB,EACjB,aAAa,EACb,SAAS,EACT,iBAAiB,EACjB,wBAAwB,EACxB,yBAAyB,EACzB,aAAa,EACb,mBAAmB,EACnB,SAAS,EACT,YAAY,EACZ,gBAAgB,GACjB,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,SAAS,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -18,6 +18,13 @@ export { auditContext } from "./interceptors/audit-context";
|
|
|
18
18
|
export { deadlineGuard } from "./interceptors/deadline-guard";
|
|
19
19
|
export { cancellationGuard } from "./interceptors/cancellation-guard";
|
|
20
20
|
export { errorMap, STANDARD_ERROR_CATALOG, } from "./interceptors/error-map";
|
|
21
|
+
// Billing/auth-specific interceptors (D423 / request 388ea83b) — the lib
|
|
22
|
+
// equivalents of billing's hand-rolled MFA + saga-envelope + saga-idempotency
|
|
23
|
+
// stack, gating the D399 createStandardServerStack migration.
|
|
24
|
+
export { withMfaRequiredUnary, createMfaPolicy, MFA_FRESHNESS_SECONDS, } from "./interceptors/mfa-required";
|
|
25
|
+
export { withSagaEnvelopeUnary } from "./interceptors/saga-envelope";
|
|
26
|
+
export { withSagaIdempotencyUnary, computeSagaIdempotencyDigest, } from "./interceptors/saga-idempotency";
|
|
27
|
+
export { parseSagaEnvelope, SAGA_ENVELOPE_HEADERS, } from "./saga-envelope";
|
|
21
28
|
// Composed-stack factory + composer.
|
|
22
29
|
export { createStandardServerStack } from "./factory";
|
|
23
30
|
export { composeUnaryInterceptors } from "./compose";
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,mDAAmD;AACnD,EAAE;AACF,gFAAgF;AAChF,EAAE;AACF,mEAAmE;AACnE,oEAAoE;AACpE,qEAAqE;AACrE,oEAAoE;AACpE,6DAA6D;AAE7D,MAAM,CAAC,MAAM,QAAQ,GAAG,mBAAmB,CAAC;AAC5C,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC;AAE/B,sBAAsB;AACtB,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,EACL,QAAQ,EACR,sBAAsB,GACvB,MAAM,0BAA0B,CAAC;AAElC,qCAAqC;AACrC,OAAO,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC;AAErD,sEAAsE;AACtE,OAAO,EACL,wBAAwB,EACxB,oBAAoB,EACpB,wBAAwB,EACxB,oBAAoB,EACpB,yBAAyB,EACzB,sBAAsB,EACtB,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,WAAW,CAAC;AAOnB,oCAAoC;AACpC,OAAO,EACL,yBAAyB,EACzB,mBAAmB,EACnB,uBAAuB,GACxB,MAAM,aAAa,CAAC;AAOrB,0BAA0B;AAC1B,OAAO,EACL,2BAA2B,EAC3B,yBAAyB,GAC1B,MAAM,kBAAkB,CAAC;AAE1B,0EAA0E;AAC1E,mCAAmC;AACnC,cAAc,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,mDAAmD;AACnD,EAAE;AACF,gFAAgF;AAChF,EAAE;AACF,mEAAmE;AACnE,oEAAoE;AACpE,qEAAqE;AACrE,oEAAoE;AACpE,6DAA6D;AAE7D,MAAM,CAAC,MAAM,QAAQ,GAAG,mBAAmB,CAAC;AAC5C,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC;AAE/B,sBAAsB;AACtB,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,EACL,QAAQ,EACR,sBAAsB,GACvB,MAAM,0BAA0B,CAAC;AAElC,yEAAyE;AACzE,8EAA8E;AAC9E,8DAA8D;AAC9D,OAAO,EACL,oBAAoB,EACpB,eAAe,EACf,qBAAqB,GACtB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAErE,OAAO,EACL,wBAAwB,EACxB,4BAA4B,GAC7B,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EACL,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,iBAAiB,CAAC;AAOzB,qCAAqC;AACrC,OAAO,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC;AAErD,sEAAsE;AACtE,OAAO,EACL,wBAAwB,EACxB,oBAAoB,EACpB,wBAAwB,EACxB,oBAAoB,EACpB,yBAAyB,EACzB,sBAAsB,EACtB,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,WAAW,CAAC;AAOnB,oCAAoC;AACpC,OAAO,EACL,yBAAyB,EACzB,mBAAmB,EACnB,uBAAuB,GACxB,MAAM,aAAa,CAAC;AAOrB,0BAA0B;AAC1B,OAAO,EACL,2BAA2B,EAC3B,yBAAyB,GAC1B,MAAM,kBAAkB,CAAC;AAE1B,0EAA0E;AAC1E,mCAAmC;AACnC,cAAc,aAAa,CAAC;AA2B5B,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,SAAS,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"enrich-auth.d.ts","sourceRoot":"","sources":["../../src/interceptors/enrich-auth.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,gBAAgB,EAGhB,gBAAgB,EACjB,MAAM,UAAU,CAAC;AA6ClB,wBAAgB,iBAAiB,CAC/B,MAAM,GAAE,gBAAqB,GAC5B,gBAAgB,
|
|
1
|
+
{"version":3,"file":"enrich-auth.d.ts","sourceRoot":"","sources":["../../src/interceptors/enrich-auth.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,gBAAgB,EAGhB,gBAAgB,EACjB,MAAM,UAAU,CAAC;AA6ClB,wBAAgB,iBAAiB,CAC/B,MAAM,GAAE,gBAAqB,GAC5B,gBAAgB,CAyElB"}
|
|
@@ -73,11 +73,22 @@ export function enrichAuthContext(config = {}) {
|
|
|
73
73
|
const tenant_id = tenantFromActor ?? tenantFromProto;
|
|
74
74
|
const intent_id = readMetadata(call, "x-nodii-intent-id");
|
|
75
75
|
const actor_type_raw = readMetadata(call, "x-nodii-actor-type");
|
|
76
|
-
|
|
76
|
+
// D436 / telemetry §5.9.1 NodiiContext mapping, applied at the interceptor so
|
|
77
|
+
// the telemetry adapter need not re-infer it: user/agent/system pass through;
|
|
78
|
+
// the edge-minted `customer` principalKind folds to the canonical `user`;
|
|
79
|
+
// `service` is derived STRUCTURALLY from an authenticated serviceActor — never
|
|
80
|
+
// the caller-controlled header (same trust rule as the actor-tenant binding
|
|
81
|
+
// above); anything else is undefined.
|
|
82
|
+
let actor_type = actor_type_raw === "user" ||
|
|
77
83
|
actor_type_raw === "agent" ||
|
|
78
84
|
actor_type_raw === "system"
|
|
79
85
|
? actor_type_raw
|
|
80
|
-
:
|
|
86
|
+
: actor_type_raw === "customer"
|
|
87
|
+
? "user"
|
|
88
|
+
: undefined;
|
|
89
|
+
if (actor_type === undefined && call.auth?.serviceActor !== undefined) {
|
|
90
|
+
actor_type = "service";
|
|
91
|
+
}
|
|
81
92
|
const required = tenantRequiredByMethod[methodName] ?? true;
|
|
82
93
|
if (required && !tenant_id) {
|
|
83
94
|
throw new GrpcStatusError("FAILED_PRECONDITION", "tenant_context_required");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"enrich-auth.js","sourceRoot":"","sources":["../../src/interceptors/enrich-auth.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"enrich-auth.js","sourceRoot":"","sources":["../../src/interceptors/enrich-auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { MfaPolicy, MfaRequiredConfig, UnaryInterceptor } from "../types";
|
|
2
|
+
/** Default freshness window — 15 minutes (matches billing `mfaRequired.ts`). */
|
|
3
|
+
export declare const MFA_FRESHNESS_SECONDS: number;
|
|
4
|
+
/**
|
|
5
|
+
* Build a configurable MFA method-policy registry. Pass the set of
|
|
6
|
+
* fully-qualified gRPC methods (`/svc/Method` and/or bare `Method`) that
|
|
7
|
+
* REQUIRE proven MFA. A method NOT in the set is treated as non-MFA
|
|
8
|
+
* (default-allow) — the registry is opt-in, mirroring billing's
|
|
9
|
+
* `MFA_REQUIRED_METHODS` registry but service-supplied.
|
|
10
|
+
*/
|
|
11
|
+
export declare function createMfaPolicy(mfaRequiredMethods?: Iterable<string>): MfaPolicy;
|
|
12
|
+
/**
|
|
13
|
+
* `withMfaRequiredUnary` — interceptor factory `(methodName, handler) ->
|
|
14
|
+
* handler`. For methods the policy marks MFA-required, rejects when MFA is
|
|
15
|
+
* absent or staler than `freshnessSeconds`; otherwise (and for every
|
|
16
|
+
* non-MFA method) passes through to the handler.
|
|
17
|
+
*/
|
|
18
|
+
export declare function withMfaRequiredUnary(config?: MfaRequiredConfig): UnaryInterceptor;
|
|
19
|
+
//# sourceMappingURL=mfa-required.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mfa-required.d.ts","sourceRoot":"","sources":["../../src/interceptors/mfa-required.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EACV,SAAS,EACT,iBAAiB,EAGjB,gBAAgB,EACjB,MAAM,UAAU,CAAC;AAGlB,gFAAgF;AAChF,eAAO,MAAM,qBAAqB,QAAU,CAAC;AAE7C;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,kBAAkB,GAAE,QAAQ,CAAC,MAAM,CAAM,GACxC,SAAS,CASX;AAiBD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,GAAE,iBAAsB,GAC7B,gBAAgB,CAwDlB"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// withMfaRequiredUnary — step-up MFA enforcement on high-privilege mutators.
|
|
2
|
+
//
|
|
3
|
+
// Gate (D423 / request 388ea83b): billing + auth decode the jose `mfa` /
|
|
4
|
+
// `mfa_at` claims on the verified S2S access token and reject MFA-required
|
|
5
|
+
// methods (the MUTATOR/MFA subset) when MFA is absent OR staler than
|
|
6
|
+
// `MFA_FRESHNESS_SECONDS`. This is the lib equivalent of billing's hand-rolled
|
|
7
|
+
// `withMfaRequiredUnary`, with the per-method policy made CALLER-CONFIGURABLE
|
|
8
|
+
// (the lib does not hardcode any service's method list).
|
|
9
|
+
//
|
|
10
|
+
// Convention (from billing `mfaRequired.ts`): the S2S-issuing gateway stamps
|
|
11
|
+
// `mfa: true` + `mfa_at: <ISO timestamp>` on the access token when an admin
|
|
12
|
+
// proves MFA. We DECODE (jose `decodeJwt`, no re-verification — verification
|
|
13
|
+
// already happened upstream in `withAuthUnary`) and read the claims directly.
|
|
14
|
+
//
|
|
15
|
+
// Wrapper order (per D399 § 5.4 + the factory's `mfaRequiredInterceptor` slot):
|
|
16
|
+
// MFA runs AFTER `withAuthUnary` (it needs the verified token) + after
|
|
17
|
+
// enrich/tenant/audit context, but BEFORE deadline/cancellation/error-map.
|
|
18
|
+
// The factory wires it at its locked slot; this is the interceptor primitive.
|
|
19
|
+
//
|
|
20
|
+
// Fail-closed: every rejection branch returns PERMISSION_DENIED — no token,
|
|
21
|
+
// decode failure, `mfa !== true`, missing `mfa_at`, or stale `mfa_at`.
|
|
22
|
+
import { decodeJwt } from "jose";
|
|
23
|
+
import { GrpcStatusError } from "../types";
|
|
24
|
+
/** Default freshness window — 15 minutes (matches billing `mfaRequired.ts`). */
|
|
25
|
+
export const MFA_FRESHNESS_SECONDS = 15 * 60;
|
|
26
|
+
/**
|
|
27
|
+
* Build a configurable MFA method-policy registry. Pass the set of
|
|
28
|
+
* fully-qualified gRPC methods (`/svc/Method` and/or bare `Method`) that
|
|
29
|
+
* REQUIRE proven MFA. A method NOT in the set is treated as non-MFA
|
|
30
|
+
* (default-allow) — the registry is opt-in, mirroring billing's
|
|
31
|
+
* `MFA_REQUIRED_METHODS` registry but service-supplied.
|
|
32
|
+
*/
|
|
33
|
+
export function createMfaPolicy(mfaRequiredMethods = []) {
|
|
34
|
+
const set = new Set(mfaRequiredMethods);
|
|
35
|
+
return {
|
|
36
|
+
requiresMfa(methodName) {
|
|
37
|
+
if (set.has(methodName))
|
|
38
|
+
return true;
|
|
39
|
+
const bare = methodName.split("/").pop() ?? methodName;
|
|
40
|
+
return set.has(bare);
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Read the raw access token off the augmented call. After `withAuthUnary`
|
|
46
|
+
* the verified token is exposed as `call.auth.rawToken` (grpc-auth S2S
|
|
47
|
+
* context). We tolerate both the nested `auth.rawToken` and a flattened
|
|
48
|
+
* `rawToken` for synthetic-call parity.
|
|
49
|
+
*/
|
|
50
|
+
function readRawToken(call) {
|
|
51
|
+
const auth = call.auth;
|
|
52
|
+
const direct = call.rawToken;
|
|
53
|
+
const v = auth?.rawToken ?? auth?.raw_token ?? direct;
|
|
54
|
+
return typeof v === "string" && v.length > 0 ? v : undefined;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* `withMfaRequiredUnary` — interceptor factory `(methodName, handler) ->
|
|
58
|
+
* handler`. For methods the policy marks MFA-required, rejects when MFA is
|
|
59
|
+
* absent or staler than `freshnessSeconds`; otherwise (and for every
|
|
60
|
+
* non-MFA method) passes through to the handler.
|
|
61
|
+
*/
|
|
62
|
+
export function withMfaRequiredUnary(config = {}) {
|
|
63
|
+
const policy = config.policy ?? createMfaPolicy(config.mfaRequiredMethods ?? []);
|
|
64
|
+
const freshnessSeconds = config.freshnessSeconds ?? MFA_FRESHNESS_SECONDS;
|
|
65
|
+
const now = config.now ?? (() => Date.now());
|
|
66
|
+
return (methodName, handler) => {
|
|
67
|
+
// Non-MFA method — pass straight through (default-allow).
|
|
68
|
+
if (!policy.requiresMfa(methodName))
|
|
69
|
+
return handler;
|
|
70
|
+
return async (call) => {
|
|
71
|
+
const token = readRawToken(call);
|
|
72
|
+
if (!token) {
|
|
73
|
+
throw new GrpcStatusError("PERMISSION_DENIED", `mfa_required: '${methodName}' requires MFA but no token claims available`);
|
|
74
|
+
}
|
|
75
|
+
let mfa;
|
|
76
|
+
let mfaAt;
|
|
77
|
+
try {
|
|
78
|
+
const claims = decodeJwt(token);
|
|
79
|
+
mfa = claims["mfa"];
|
|
80
|
+
mfaAt = claims["mfa_at"];
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
throw new GrpcStatusError("PERMISSION_DENIED", "mfa_required: token decode failed");
|
|
84
|
+
}
|
|
85
|
+
if (mfa !== true) {
|
|
86
|
+
throw new GrpcStatusError("PERMISSION_DENIED", `mfa_required: '${methodName}' requires mfa=true claim`);
|
|
87
|
+
}
|
|
88
|
+
if (typeof mfaAt !== "string") {
|
|
89
|
+
throw new GrpcStatusError("PERMISSION_DENIED", "mfa_required: missing mfa_at claim");
|
|
90
|
+
}
|
|
91
|
+
const ageMs = now() - Date.parse(mfaAt);
|
|
92
|
+
if (!Number.isFinite(ageMs) || ageMs / 1000 > freshnessSeconds) {
|
|
93
|
+
throw new GrpcStatusError("PERMISSION_DENIED", `mfa_stale: mfa_at older than ${freshnessSeconds}s`);
|
|
94
|
+
}
|
|
95
|
+
// Fresh MFA — proceed.
|
|
96
|
+
return handler(call);
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=mfa-required.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mfa-required.js","sourceRoot":"","sources":["../../src/interceptors/mfa-required.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type SagaEnvelope } from "../saga-envelope";
|
|
2
|
+
import type { UnaryCall, UnaryInterceptor } from "../types";
|
|
3
|
+
/** Augmented call that carries the parsed full saga envelope. */
|
|
4
|
+
export interface CallWithSagaEnvelope extends UnaryCall {
|
|
5
|
+
sagaEnvelope?: SagaEnvelope;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* `withSagaEnvelopeUnary` — interceptor factory. Attaches `aug.sagaEnvelope`
|
|
9
|
+
* when a full envelope is on the wire; leaves it `undefined` for non-saga
|
|
10
|
+
* calls; rejects malformed envelopes with INVALID_ARGUMENT.
|
|
11
|
+
*/
|
|
12
|
+
export declare function withSagaEnvelopeUnary(): UnaryInterceptor;
|
|
13
|
+
//# sourceMappingURL=saga-envelope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"saga-envelope.d.ts","sourceRoot":"","sources":["../../src/interceptors/saga-envelope.ts"],"names":[],"mappings":"AAmBA,OAAO,EACL,KAAK,YAAY,EAGlB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,SAAS,EAAgB,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAG1E,iEAAiE;AACjE,MAAM,WAAW,oBAAqB,SAAQ,SAAS;IACrD,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AA+BD;;;;GAIG;AACH,wBAAgB,qBAAqB,IAAI,gBAAgB,CA0BxD"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// withSagaEnvelopeUnary — parse the 7 `x-saga-*` headers off the inbound gRPC
|
|
2
|
+
// metadata into a full `SagaEnvelope` attached to the call as
|
|
3
|
+
// `aug.sagaEnvelope`.
|
|
4
|
+
//
|
|
5
|
+
// Gate (D423 / request 388ea83b): billing's `withSagaIdempotency` +
|
|
6
|
+
// saga-aware idempotency leg depend on the FULL envelope (sagaId, stepId,
|
|
7
|
+
// sagaKind, orchestrator, issuedAt, timeoutAt, traceId?) — not just the
|
|
8
|
+
// re-exported generic `sagaContext` which only carries the partial
|
|
9
|
+
// `x-saga-id`/`x-step-id` pair. This interceptor is the lib equivalent of
|
|
10
|
+
// billing's hand-rolled `withSagaEnvelopeUnary`.
|
|
11
|
+
//
|
|
12
|
+
// Behavior:
|
|
13
|
+
// - No saga headers on the wire → `aug.sagaEnvelope === undefined`
|
|
14
|
+
// (a non-saga call) — the handler runs unchanged.
|
|
15
|
+
// - All 7 headers present + valid → the full envelope is attached.
|
|
16
|
+
// - A PARTIAL or INVALID envelope (some-but-not-all fields, bad UUID, bad
|
|
17
|
+
// timestamp, timeout_at <= issued_at) → INVALID_ARGUMENT (fail-loud; the
|
|
18
|
+
// orchestrator emitted a malformed envelope).
|
|
19
|
+
import { parseSagaEnvelope, } from "../saga-envelope";
|
|
20
|
+
import { GrpcStatusError } from "../types";
|
|
21
|
+
/** Read a single header string off the loose `UnaryCall` metadata shape. */
|
|
22
|
+
function readMeta(call, key) {
|
|
23
|
+
const list = call.metadata.get(key);
|
|
24
|
+
if (!list || list.length === 0)
|
|
25
|
+
return undefined;
|
|
26
|
+
const v = list[0];
|
|
27
|
+
if (typeof v === "string")
|
|
28
|
+
return v;
|
|
29
|
+
if (v instanceof Uint8Array)
|
|
30
|
+
return Buffer.from(v).toString("utf8");
|
|
31
|
+
if (v != null &&
|
|
32
|
+
typeof v.toString === "function") {
|
|
33
|
+
return v.toString();
|
|
34
|
+
}
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
function parseErrorDetail(err) {
|
|
38
|
+
switch (err.kind) {
|
|
39
|
+
case "missing":
|
|
40
|
+
return `saga_envelope_incomplete: ${err.field} is required when other envelope fields are present`;
|
|
41
|
+
case "invalid_uuid":
|
|
42
|
+
return `saga_envelope_invalid_uuid: ${err.field} = '${err.value}'`;
|
|
43
|
+
case "invalid_timestamp":
|
|
44
|
+
return `saga_envelope_invalid_timestamp: ${err.field} = '${err.value}' (expected ISO 8601 UTC)`;
|
|
45
|
+
case "timeout_before_issued":
|
|
46
|
+
return `saga_envelope_timeout_before_issued: issued_at=${err.issuedAt} timeout_at=${err.timeoutAt}`;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* `withSagaEnvelopeUnary` — interceptor factory. Attaches `aug.sagaEnvelope`
|
|
51
|
+
* when a full envelope is on the wire; leaves it `undefined` for non-saga
|
|
52
|
+
* calls; rejects malformed envelopes with INVALID_ARGUMENT.
|
|
53
|
+
*/
|
|
54
|
+
export function withSagaEnvelopeUnary() {
|
|
55
|
+
return (_methodName, handler) => async (call) => {
|
|
56
|
+
const parsed = parseSagaEnvelope({
|
|
57
|
+
sagaId: readMeta(call, "x-saga-id"),
|
|
58
|
+
stepId: readMeta(call, "x-step-id"),
|
|
59
|
+
sagaKind: readMeta(call, "x-saga-kind"),
|
|
60
|
+
orchestrator: readMeta(call, "x-saga-orchestrator"),
|
|
61
|
+
issuedAt: readMeta(call, "x-saga-issued-at"),
|
|
62
|
+
timeoutAt: readMeta(call, "x-saga-timeout-at"),
|
|
63
|
+
traceId: readMeta(call, "x-saga-trace-id"),
|
|
64
|
+
});
|
|
65
|
+
// No envelope on the wire — non-saga call, run handler unchanged.
|
|
66
|
+
if (parsed === null) {
|
|
67
|
+
return handler(call);
|
|
68
|
+
}
|
|
69
|
+
if (!parsed.ok) {
|
|
70
|
+
throw new GrpcStatusError("INVALID_ARGUMENT", parseErrorDetail(parsed.error));
|
|
71
|
+
}
|
|
72
|
+
call.sagaEnvelope = parsed.envelope;
|
|
73
|
+
return handler(call);
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=saga-envelope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"saga-envelope.js","sourceRoot":"","sources":["../../src/interceptors/saga-envelope.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { SagaEnvelope } from "../saga-envelope";
|
|
2
|
+
import type { UnaryCall, UnaryInterceptor } from "../types";
|
|
3
|
+
export interface SagaIdempotencyConfig {
|
|
4
|
+
/**
|
|
5
|
+
* Resolve the AUTHORITATIVE tenant for the call. Defaults to the enriched
|
|
6
|
+
* `auth.requestContext.tenant_id` (set by `enrichAuthContext`) falling back
|
|
7
|
+
* to the saga envelope's `x-saga-tenant-id` is NOT used — the envelope does
|
|
8
|
+
* not carry a tenant; the tenant comes from the verified actor. A tenant-less
|
|
9
|
+
* saga call FAILS CLOSED (D404).
|
|
10
|
+
*/
|
|
11
|
+
getTenantId?: (call: UnaryCall) => string | undefined;
|
|
12
|
+
/** Header name the caller would (wrongly) use to supply a key. */
|
|
13
|
+
headerName?: string;
|
|
14
|
+
/** TTL (seconds) for the claim entry. Defaults to library config. */
|
|
15
|
+
ttlSeconds?: number;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Build the canonical saga idempotency key from `(tenant_id, method,
|
|
19
|
+
* saga_id, step_id)`. Exported for parity-fence byte-equivalence tests.
|
|
20
|
+
*/
|
|
21
|
+
export declare function computeSagaIdempotencyDigest(tenantId: string, method: string, envelope: Pick<SagaEnvelope, "sagaId" | "stepId">): string;
|
|
22
|
+
/**
|
|
23
|
+
* `withSagaIdempotencyUnary` — interceptor factory. Engages only when the call
|
|
24
|
+
* carries a full saga envelope; rejects a caller idempotency-key; otherwise
|
|
25
|
+
* CLAIMs the canonical key over the service idempotency store with
|
|
26
|
+
* replay/in-flight semantics; fail-closed on a store outage.
|
|
27
|
+
*/
|
|
28
|
+
export declare function withSagaIdempotencyUnary(config?: SagaIdempotencyConfig): UnaryInterceptor;
|
|
29
|
+
//# sourceMappingURL=saga-idempotency.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"saga-idempotency.d.ts","sourceRoot":"","sources":["../../src/interceptors/saga-idempotency.ts"],"names":[],"mappings":"AAkCA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EAAE,SAAS,EAAgB,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAM1E,MAAM,WAAW,qBAAqB;IACpC;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,MAAM,GAAG,SAAS,CAAC;IACtD,kEAAkE;IAClE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAgCD;;;GAGG;AACH,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,IAAI,CAAC,YAAY,EAAE,QAAQ,GAAG,QAAQ,CAAC,GAChD,MAAM,CAMR;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,GAAE,qBAA0B,GACjC,gBAAgB,CAqIlB"}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
// withSagaIdempotencyUnary — saga-aware idempotency keying leg.
|
|
2
|
+
//
|
|
3
|
+
// Gate (D423 / request 388ea83b): a saga-participant step's idempotency key is
|
|
4
|
+
// owned by the SAGA ENVELOPE, not the caller. This leg, for calls carrying a
|
|
5
|
+
// full `aug.sagaEnvelope` (parsed by `withSagaEnvelopeUnary`), builds a
|
|
6
|
+
// CANONICAL key from `(tenant_id, method, saga_id, step_id)` and CLAIMs it over
|
|
7
|
+
// the SERVICE's own idempotency store (the `@nodii/idempotency` CLAIM path /
|
|
8
|
+
// Redis Lua). It REJECTS a caller-supplied idempotency-key header on a saga
|
|
9
|
+
// call — the envelope owns the key.
|
|
10
|
+
//
|
|
11
|
+
// Canonical key (reconciles comm-doctrine § 7.3 D163 + D404 tenant-scoping):
|
|
12
|
+
// digest = sha256(tenant_id + method + (saga_id + "|" + step_id))
|
|
13
|
+
// via computeIdempotencyKey({sagaId: tenant_id,
|
|
14
|
+
// stepName: method, serializedInput: saga_id|step_id})
|
|
15
|
+
// redis key = nodii:<service>:saga:idempotency:saga:<digest>
|
|
16
|
+
// (tenant folded into the digest per D404 — fail-closed if tenant absent)
|
|
17
|
+
//
|
|
18
|
+
// NON-saga calls (no envelope) pass through untouched — the standard
|
|
19
|
+
// `idempotencyGuard` leg (caller header `x-nodii-idempotency-key`) handles
|
|
20
|
+
// non-saga mutators. This leg ONLY engages when `aug.sagaEnvelope` is set.
|
|
21
|
+
//
|
|
22
|
+
// FAULT-INJECTION posture (Pillar 5): the idempotency store (Redis) is an
|
|
23
|
+
// external dependency. A CLAIM-time store outage FAILS CLOSED — the exception
|
|
24
|
+
// propagates (mapped downstream to UNAVAILABLE by errorMap) and the handler is
|
|
25
|
+
// NEVER run without the dedupe guarantee. This matches `@nodii/idempotency`'s
|
|
26
|
+
// own posture: only logical outcomes (completed / in_flight / failed) are
|
|
27
|
+
// return values; an escaped exception is by definition infra → fail closed.
|
|
28
|
+
import { assembleSagaStepKey, computeIdempotencyKey, getIdempotencyOrNull, } from "@nodii/idempotency";
|
|
29
|
+
import { GrpcStatusError } from "../types";
|
|
30
|
+
/** Caller-supplied idempotency-key header name (comm-doctrine § 7.4). */
|
|
31
|
+
const DEFAULT_HEADER = "x-nodii-idempotency-key";
|
|
32
|
+
function defaultTenantId(call) {
|
|
33
|
+
const auth = call.auth;
|
|
34
|
+
const fromCtx = auth?.requestContext?.tenant_id;
|
|
35
|
+
if (typeof fromCtx === "string" && fromCtx.length > 0)
|
|
36
|
+
return fromCtx;
|
|
37
|
+
const fromUser = auth?.userActor?.tenant_id;
|
|
38
|
+
if (typeof fromUser === "string" && fromUser.length > 0)
|
|
39
|
+
return fromUser;
|
|
40
|
+
if (typeof auth?.tenantId === "string" && auth.tenantId.length > 0) {
|
|
41
|
+
return auth.tenantId;
|
|
42
|
+
}
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
function readHeader(call, name) {
|
|
46
|
+
const list = call.metadata.get(name);
|
|
47
|
+
if (!list || list.length === 0)
|
|
48
|
+
return undefined;
|
|
49
|
+
const v = list[0];
|
|
50
|
+
if (typeof v === "string")
|
|
51
|
+
return v.length > 0 ? v : undefined;
|
|
52
|
+
if (v instanceof Uint8Array) {
|
|
53
|
+
const s = Buffer.from(v).toString("utf8");
|
|
54
|
+
return s.length > 0 ? s : undefined;
|
|
55
|
+
}
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Build the canonical saga idempotency key from `(tenant_id, method,
|
|
60
|
+
* saga_id, step_id)`. Exported for parity-fence byte-equivalence tests.
|
|
61
|
+
*/
|
|
62
|
+
export function computeSagaIdempotencyDigest(tenantId, method, envelope) {
|
|
63
|
+
return computeIdempotencyKey({
|
|
64
|
+
sagaId: tenantId,
|
|
65
|
+
stepName: method,
|
|
66
|
+
serializedInput: `${envelope.sagaId}|${envelope.stepId}`,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* `withSagaIdempotencyUnary` — interceptor factory. Engages only when the call
|
|
71
|
+
* carries a full saga envelope; rejects a caller idempotency-key; otherwise
|
|
72
|
+
* CLAIMs the canonical key over the service idempotency store with
|
|
73
|
+
* replay/in-flight semantics; fail-closed on a store outage.
|
|
74
|
+
*/
|
|
75
|
+
export function withSagaIdempotencyUnary(config = {}) {
|
|
76
|
+
const headerName = config.headerName ?? DEFAULT_HEADER;
|
|
77
|
+
const getTenantId = config.getTenantId ?? defaultTenantId;
|
|
78
|
+
return (methodName, handler) => async (call) => {
|
|
79
|
+
const envelope = call.sagaEnvelope;
|
|
80
|
+
// No saga envelope → non-saga path; the standard idempotency leg owns it.
|
|
81
|
+
if (!envelope)
|
|
82
|
+
return handler(call);
|
|
83
|
+
// The saga envelope OWNS the key. A caller idempotency-key on a saga call
|
|
84
|
+
// is a contract violation (two competing key owners).
|
|
85
|
+
const callerKey = readHeader(call, headerName);
|
|
86
|
+
if (callerKey) {
|
|
87
|
+
throw new GrpcStatusError("INVALID_ARGUMENT", "idempotency_key_conflict_with_saga: do not pass an idempotency-key header when a saga envelope is present");
|
|
88
|
+
}
|
|
89
|
+
// Tenant is folded into the digest (D404) — fail closed if absent.
|
|
90
|
+
const tenantId = getTenantId(call);
|
|
91
|
+
if (!tenantId) {
|
|
92
|
+
throw new GrpcStatusError("INVALID_ARGUMENT", "idempotency_tenant_required: saga idempotency key needs a resolved tenant");
|
|
93
|
+
}
|
|
94
|
+
const cfg = getIdempotencyOrNull();
|
|
95
|
+
if (!cfg) {
|
|
96
|
+
// `initIdempotency` must run at boot before the stack handles a saga
|
|
97
|
+
// call. Missing init is a wiring bug — fail loud, never run unguarded.
|
|
98
|
+
throw new GrpcStatusError("INTERNAL", "saga_idempotency_not_initialized: call initIdempotency() at boot");
|
|
99
|
+
}
|
|
100
|
+
const digest = computeSagaIdempotencyDigest(tenantId, methodName, envelope);
|
|
101
|
+
const key = assembleSagaStepKey(cfg.serviceId, digest, "saga");
|
|
102
|
+
const client = cfg.redisClient;
|
|
103
|
+
if (typeof client.claim !== "function") {
|
|
104
|
+
throw new GrpcStatusError("INTERNAL", "saga_idempotency_requires_claim_capable_client");
|
|
105
|
+
}
|
|
106
|
+
const nowMs = Date.now();
|
|
107
|
+
const ttl = config.ttlSeconds ?? cfg.defaultTtlSeconds;
|
|
108
|
+
const inflightPayload = JSON.stringify({
|
|
109
|
+
status: "in_flight",
|
|
110
|
+
created_at: new Date(nowMs).toISOString(),
|
|
111
|
+
created_at_ms: nowMs,
|
|
112
|
+
});
|
|
113
|
+
// CLAIM — a store outage here THROWS and propagates: FAIL CLOSED. The
|
|
114
|
+
// handler is never run without the dedupe guarantee (errorMap → UNAVAILABLE).
|
|
115
|
+
const outcome = await client.claim(key, inflightPayload, ttl); // fault-injection:allow @nodii/idempotency store op — store-down is fault-injection tested (FailingRedisClient.claim raises -> fail-closed, handler never runs); Pillar 5 mis-categorizes the idempotency-store client as grpc (categorization follow-up tracked).
|
|
116
|
+
if (outcome.state === "completed") {
|
|
117
|
+
// Idempotent replay — return the cached result without re-running.
|
|
118
|
+
return outcome.result;
|
|
119
|
+
}
|
|
120
|
+
if (outcome.state === "failed") {
|
|
121
|
+
// Cached application-class failure — replay the same rejection.
|
|
122
|
+
const err = outcome.error;
|
|
123
|
+
throw new GrpcStatusError("ABORTED", typeof err === "object" && err && "message" in err
|
|
124
|
+
? String(err.message)
|
|
125
|
+
: "saga_idempotency_cached_failure");
|
|
126
|
+
}
|
|
127
|
+
if (outcome.state === "in_flight") {
|
|
128
|
+
throw new GrpcStatusError("ABORTED", "idempotency_in_flight");
|
|
129
|
+
}
|
|
130
|
+
// Acquired — run the handler, then COMPLETE (or release on error).
|
|
131
|
+
let res;
|
|
132
|
+
try {
|
|
133
|
+
res = await handler(call);
|
|
134
|
+
}
|
|
135
|
+
catch (err) {
|
|
136
|
+
// Best-effort release so a retry can re-claim the slot.
|
|
137
|
+
try {
|
|
138
|
+
await client.del(key); // fault-injection:allow @nodii/idempotency store op — store-down is fault-injection tested (FailingRedisClient.claim raises -> fail-closed, handler never runs); Pillar 5 mis-categorizes the idempotency-store client as grpc (categorization follow-up tracked).
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
// best-effort
|
|
142
|
+
}
|
|
143
|
+
throw err;
|
|
144
|
+
}
|
|
145
|
+
// POST-HANDLER completion persist. The handler ALREADY ran + succeeded;
|
|
146
|
+
// its side-effects are committed. A store fault HERE must NOT convert a
|
|
147
|
+
// successful call into a client-visible failure (and must not pin the
|
|
148
|
+
// slot `in_flight` until TTL, which would make the legitimate retry hit
|
|
149
|
+
// `idempotency_in_flight`). So persisting the completion is BEST-EFFORT:
|
|
150
|
+
// on a store fault we release the in_flight slot (so a retry can re-claim
|
|
151
|
+
// + re-run — at most one redundant execution, the documented dedupe-window
|
|
152
|
+
// tradeoff) and return the successful result. This intentional behavior is
|
|
153
|
+
// fault-injection tested (see mfa-saga.test.ts "FAULT: store-down on the
|
|
154
|
+
// POST-handler completion path").
|
|
155
|
+
const completedPayload = JSON.stringify({
|
|
156
|
+
status: "completed",
|
|
157
|
+
result: res,
|
|
158
|
+
completed_at: new Date().toISOString(),
|
|
159
|
+
created_at_ms: nowMs,
|
|
160
|
+
outcome_class: "application",
|
|
161
|
+
});
|
|
162
|
+
try {
|
|
163
|
+
if (typeof client.complete === "function") {
|
|
164
|
+
await client.complete(key, completedPayload, ttl); // fault-injection:allow @nodii/idempotency store op — store-down on the POST-handler completion path is fault-injection tested (best-effort: handler result preserved, in_flight slot released, no crash); Pillar 5 mis-categorizes the idempotency-store client as grpc (categorization follow-up tracked).
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
await client.del(key); // fault-injection:allow @nodii/idempotency store op — store-down on the POST-handler completion path is fault-injection tested (best-effort: handler result preserved, in_flight slot released, no crash); Pillar 5 mis-categorizes the idempotency-store client as grpc (categorization follow-up tracked).
|
|
168
|
+
await client.setNX(key, completedPayload, ttl); // fault-injection:allow @nodii/idempotency store op — store-down on the POST-handler completion path is fault-injection tested (best-effort: handler result preserved, in_flight slot released, no crash); Pillar 5 mis-categorizes the idempotency-store client as grpc (categorization follow-up tracked).
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
// Best-effort: the handler succeeded; do NOT fail the call. Release the
|
|
173
|
+
// in_flight slot so a retry can re-claim (re-run is idempotency-safe).
|
|
174
|
+
try {
|
|
175
|
+
await client.del(key); // fault-injection:allow @nodii/idempotency store op — store-down on the POST-handler completion path is fault-injection tested (best-effort: handler result preserved, in_flight slot released, no crash); Pillar 5 mis-categorizes the idempotency-store client as grpc (categorization follow-up tracked).
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
// best-effort
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return res;
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=saga-idempotency.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"saga-idempotency.js","sourceRoot":"","sources":["../../src/interceptors/saga-idempotency.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/** The full saga envelope attached to the call by `withSagaEnvelopeUnary`. */
|
|
2
|
+
export interface SagaEnvelope {
|
|
3
|
+
/** Orchestrator-generated UUID, stable across the whole saga. */
|
|
4
|
+
sagaId: string;
|
|
5
|
+
/** Orchestrator-generated UUID, unique per participant step. */
|
|
6
|
+
stepId: string;
|
|
7
|
+
/** Saga kind from the sagas.json registry. */
|
|
8
|
+
sagaKind: string;
|
|
9
|
+
/** Service id of the orchestrator (`tenant`, `billing`, …). */
|
|
10
|
+
orchestrator: string;
|
|
11
|
+
/** When the orchestrator issued this RPC. */
|
|
12
|
+
issuedAt: Date;
|
|
13
|
+
/**
|
|
14
|
+
* When the orchestrator considers this step timed out. Participants SHOULD
|
|
15
|
+
* short-circuit if past it.
|
|
16
|
+
*/
|
|
17
|
+
timeoutAt: Date;
|
|
18
|
+
/** Optional W3C traceparent — pinned to the per-step OTel span. */
|
|
19
|
+
traceId?: string;
|
|
20
|
+
}
|
|
21
|
+
export type SagaEnvelopeParseError = {
|
|
22
|
+
kind: "missing";
|
|
23
|
+
field: string;
|
|
24
|
+
} | {
|
|
25
|
+
kind: "invalid_uuid";
|
|
26
|
+
field: string;
|
|
27
|
+
value: string;
|
|
28
|
+
} | {
|
|
29
|
+
kind: "invalid_timestamp";
|
|
30
|
+
field: string;
|
|
31
|
+
value: string;
|
|
32
|
+
} | {
|
|
33
|
+
kind: "timeout_before_issued";
|
|
34
|
+
issuedAt: string;
|
|
35
|
+
timeoutAt: string;
|
|
36
|
+
};
|
|
37
|
+
/** The 7 `x-saga-*` headers, in canonical order. */
|
|
38
|
+
export declare const SAGA_ENVELOPE_HEADERS: {
|
|
39
|
+
readonly sagaId: "x-saga-id";
|
|
40
|
+
readonly stepId: "x-step-id";
|
|
41
|
+
readonly sagaKind: "x-saga-kind";
|
|
42
|
+
readonly orchestrator: "x-saga-orchestrator";
|
|
43
|
+
readonly issuedAt: "x-saga-issued-at";
|
|
44
|
+
readonly timeoutAt: "x-saga-timeout-at";
|
|
45
|
+
readonly traceId: "x-saga-trace-id";
|
|
46
|
+
};
|
|
47
|
+
export interface SagaEnvelopeHeaders {
|
|
48
|
+
sagaId?: string;
|
|
49
|
+
stepId?: string;
|
|
50
|
+
sagaKind?: string;
|
|
51
|
+
orchestrator?: string;
|
|
52
|
+
issuedAt?: string;
|
|
53
|
+
timeoutAt?: string;
|
|
54
|
+
traceId?: string;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Build a SagaEnvelope from the raw header strings. Returns the envelope on
|
|
58
|
+
* success, or a typed error describing the first validation failure.
|
|
59
|
+
*
|
|
60
|
+
* Returns `null` when NONE of the required envelope fields are present —
|
|
61
|
+
* caller treats that as "no envelope sent" (a non-saga call).
|
|
62
|
+
*/
|
|
63
|
+
export declare function parseSagaEnvelope(headers: SagaEnvelopeHeaders): {
|
|
64
|
+
ok: true;
|
|
65
|
+
envelope: SagaEnvelope;
|
|
66
|
+
} | {
|
|
67
|
+
ok: false;
|
|
68
|
+
error: SagaEnvelopeParseError;
|
|
69
|
+
} | null;
|
|
70
|
+
//# sourceMappingURL=saga-envelope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"saga-envelope.d.ts","sourceRoot":"","sources":["../src/saga-envelope.ts"],"names":[],"mappings":"AAyBA,8EAA8E;AAC9E,MAAM,WAAW,YAAY;IAC3B,iEAAiE;IACjE,MAAM,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,MAAM,EAAE,MAAM,CAAC;IACf,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,+DAA+D;IAC/D,YAAY,EAAE,MAAM,CAAC;IACrB,6CAA6C;IAC7C,QAAQ,EAAE,IAAI,CAAC;IACf;;;OAGG;IACH,SAAS,EAAE,IAAI,CAAC;IAChB,mEAAmE;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,sBAAsB,GAC9B;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACtD;IAAE,IAAI,EAAE,mBAAmB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC3D;IAAE,IAAI,EAAE,uBAAuB,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;AAM3E,oDAAoD;AACpD,eAAO,MAAM,qBAAqB;;;;;;;;CAQxB,CAAC;AAEX,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,mBAAmB,GAE1B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,YAAY,CAAA;CAAE,GACpC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,sBAAsB,CAAA;CAAE,GAC5C,IAAI,CA2FP"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
// SagaEnvelope — the doctrine-locked metadata carried on every saga-participant
|
|
2
|
+
// gRPC RPC. Wire format on gRPC metadata is discrete per-field headers (NOT a
|
|
3
|
+
// single JSON blob), to match the existing partial-envelope `x-saga-id` /
|
|
4
|
+
// `x-step-id` headers handled by the saga context interceptor. The full
|
|
5
|
+
// envelope is 7 `x-saga-*` headers:
|
|
6
|
+
//
|
|
7
|
+
// x-saga-id — UUID (orchestrator-stable saga id)
|
|
8
|
+
// x-step-id — UUID (unique per participant step)
|
|
9
|
+
// x-saga-kind — string from the sagas.json registry
|
|
10
|
+
// x-saga-orchestrator — service id of the orchestrator
|
|
11
|
+
// x-saga-issued-at — ISO 8601 UTC
|
|
12
|
+
// x-saga-timeout-at — ISO 8601 UTC
|
|
13
|
+
// x-saga-trace-id — W3C traceparent (OPTIONAL)
|
|
14
|
+
//
|
|
15
|
+
// Per comm-doctrine § 5.1 (`x-nodii-saga-id`, `x-nodii-saga-trace-id`) the
|
|
16
|
+
// auto-injected client headers exist; the FULL per-step envelope (this file)
|
|
17
|
+
// is the participant-side IPC contract billing's `withSagaIdempotency` depends
|
|
18
|
+
// on. Backwards compat: when ONLY `x-saga-id` + `x-step-id` are present, the
|
|
19
|
+
// legacy saga-context interceptor handles them (no envelope built); when all
|
|
20
|
+
// required envelope fields are present, `withSagaEnvelopeUnary` builds the full
|
|
21
|
+
// envelope and attaches it to the call as `aug.sagaEnvelope`.
|
|
22
|
+
//
|
|
23
|
+
// Ported from billing's `src/lib/saga/SagaEnvelope.ts` into the lib so every
|
|
24
|
+
// adopter gets the identical parse from `@nodii/grpc-interceptors`.
|
|
25
|
+
// UUID v1-v8. Matches the other interceptors' regex shape.
|
|
26
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
27
|
+
/** The 7 `x-saga-*` headers, in canonical order. */
|
|
28
|
+
export const SAGA_ENVELOPE_HEADERS = {
|
|
29
|
+
sagaId: "x-saga-id",
|
|
30
|
+
stepId: "x-step-id",
|
|
31
|
+
sagaKind: "x-saga-kind",
|
|
32
|
+
orchestrator: "x-saga-orchestrator",
|
|
33
|
+
issuedAt: "x-saga-issued-at",
|
|
34
|
+
timeoutAt: "x-saga-timeout-at",
|
|
35
|
+
traceId: "x-saga-trace-id",
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Build a SagaEnvelope from the raw header strings. Returns the envelope on
|
|
39
|
+
* success, or a typed error describing the first validation failure.
|
|
40
|
+
*
|
|
41
|
+
* Returns `null` when NONE of the required envelope fields are present —
|
|
42
|
+
* caller treats that as "no envelope sent" (a non-saga call).
|
|
43
|
+
*/
|
|
44
|
+
export function parseSagaEnvelope(headers) {
|
|
45
|
+
// None of the required fields present → no envelope on the wire.
|
|
46
|
+
const anyPresent = headers.sagaId !== undefined ||
|
|
47
|
+
headers.stepId !== undefined ||
|
|
48
|
+
headers.sagaKind !== undefined ||
|
|
49
|
+
headers.orchestrator !== undefined ||
|
|
50
|
+
headers.issuedAt !== undefined ||
|
|
51
|
+
headers.timeoutAt !== undefined;
|
|
52
|
+
if (!anyPresent)
|
|
53
|
+
return null;
|
|
54
|
+
// When ANY required field is present, ALL of them must be present — a
|
|
55
|
+
// partial envelope is a wire-contract violation.
|
|
56
|
+
for (const [field, value] of [
|
|
57
|
+
["saga_id", headers.sagaId],
|
|
58
|
+
["step_id", headers.stepId],
|
|
59
|
+
["saga_kind", headers.sagaKind],
|
|
60
|
+
["orchestrator", headers.orchestrator],
|
|
61
|
+
["issued_at", headers.issuedAt],
|
|
62
|
+
["timeout_at", headers.timeoutAt],
|
|
63
|
+
]) {
|
|
64
|
+
if (value === undefined || value.length === 0) {
|
|
65
|
+
return { ok: false, error: { kind: "missing", field } };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const sagaId = headers.sagaId;
|
|
69
|
+
const stepId = headers.stepId;
|
|
70
|
+
const sagaKind = headers.sagaKind;
|
|
71
|
+
const orchestrator = headers.orchestrator;
|
|
72
|
+
const issuedAtRaw = headers.issuedAt;
|
|
73
|
+
const timeoutAtRaw = headers.timeoutAt;
|
|
74
|
+
if (!UUID_RE.test(sagaId)) {
|
|
75
|
+
return {
|
|
76
|
+
ok: false,
|
|
77
|
+
error: { kind: "invalid_uuid", field: "saga_id", value: sagaId },
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
if (!UUID_RE.test(stepId)) {
|
|
81
|
+
return {
|
|
82
|
+
ok: false,
|
|
83
|
+
error: { kind: "invalid_uuid", field: "step_id", value: stepId },
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
const issuedAt = new Date(issuedAtRaw);
|
|
87
|
+
if (Number.isNaN(issuedAt.getTime())) {
|
|
88
|
+
return {
|
|
89
|
+
ok: false,
|
|
90
|
+
error: {
|
|
91
|
+
kind: "invalid_timestamp",
|
|
92
|
+
field: "issued_at",
|
|
93
|
+
value: issuedAtRaw,
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
const timeoutAt = new Date(timeoutAtRaw);
|
|
98
|
+
if (Number.isNaN(timeoutAt.getTime())) {
|
|
99
|
+
return {
|
|
100
|
+
ok: false,
|
|
101
|
+
error: {
|
|
102
|
+
kind: "invalid_timestamp",
|
|
103
|
+
field: "timeout_at",
|
|
104
|
+
value: timeoutAtRaw,
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
if (timeoutAt.getTime() <= issuedAt.getTime()) {
|
|
109
|
+
return {
|
|
110
|
+
ok: false,
|
|
111
|
+
error: {
|
|
112
|
+
kind: "timeout_before_issued",
|
|
113
|
+
issuedAt: issuedAtRaw,
|
|
114
|
+
timeoutAt: timeoutAtRaw,
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
ok: true,
|
|
120
|
+
envelope: {
|
|
121
|
+
sagaId,
|
|
122
|
+
stepId,
|
|
123
|
+
sagaKind,
|
|
124
|
+
orchestrator,
|
|
125
|
+
issuedAt,
|
|
126
|
+
timeoutAt,
|
|
127
|
+
traceId: headers.traceId,
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=saga-envelope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"saga-envelope.js","sourceRoot":"","sources":["../src/saga-envelope.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -38,6 +38,12 @@ export interface UnaryCall {
|
|
|
38
38
|
* mutates onto the call.
|
|
39
39
|
*/
|
|
40
40
|
export interface AuthOnCall {
|
|
41
|
+
/**
|
|
42
|
+
* The verified raw access token (set by `withAuthUnary`). The MFA leg
|
|
43
|
+
* decodes it (jose, no re-verification — verification already happened in
|
|
44
|
+
* `withAuthUnary`) to read the `mfa` / `mfa_at` step-up claims.
|
|
45
|
+
*/
|
|
46
|
+
rawToken?: string;
|
|
41
47
|
/** Service-to-service actor stamp. */
|
|
42
48
|
serviceActor?: {
|
|
43
49
|
service_id?: string;
|
|
@@ -63,7 +69,7 @@ export interface AuthOnCall {
|
|
|
63
69
|
requestContext?: {
|
|
64
70
|
tenant_id?: string;
|
|
65
71
|
intent_id?: string;
|
|
66
|
-
actor_type?: "user" | "agent" | "system";
|
|
72
|
+
actor_type?: "user" | "agent" | "system" | "service";
|
|
67
73
|
correlation_id?: string;
|
|
68
74
|
};
|
|
69
75
|
}
|
|
@@ -186,6 +192,35 @@ export interface AuditConfig {
|
|
|
186
192
|
/** Methods skipped entirely (e.g. health checks). */
|
|
187
193
|
skipMethods?: ReadonlySet<string>;
|
|
188
194
|
}
|
|
195
|
+
/**
|
|
196
|
+
* MFA method-policy — decides which methods require proven step-up MFA.
|
|
197
|
+
* Caller-configurable: the lib hardcodes no service's method list.
|
|
198
|
+
*/
|
|
199
|
+
export interface MfaPolicy {
|
|
200
|
+
requiresMfa(methodName: string): boolean;
|
|
201
|
+
}
|
|
202
|
+
/** Config for `withMfaRequiredUnary` — spec D423 / request 388ea83b. */
|
|
203
|
+
export interface MfaRequiredConfig {
|
|
204
|
+
/**
|
|
205
|
+
* Fully-qualified (`/svc/Method`) and/or bare (`Method`) gRPC methods that
|
|
206
|
+
* REQUIRE proven MFA. Used to build the default `MfaPolicy` when `policy`
|
|
207
|
+
* is not supplied. A method absent from the set is non-MFA (default-allow).
|
|
208
|
+
*/
|
|
209
|
+
mfaRequiredMethods?: Iterable<string>;
|
|
210
|
+
/** A pre-built policy — takes precedence over `mfaRequiredMethods`. */
|
|
211
|
+
policy?: MfaPolicy;
|
|
212
|
+
/** Freshness window in seconds. Default 900 (15 min). */
|
|
213
|
+
freshnessSeconds?: number;
|
|
214
|
+
/** Clock override for deterministic tests. Default `Date.now`. */
|
|
215
|
+
now?: () => number;
|
|
216
|
+
}
|
|
217
|
+
/** Config for `withSagaIdempotencyUnary` — spec D423 / request 388ea83b. */
|
|
218
|
+
export interface SagaIdempotencyLegConfig {
|
|
219
|
+
/** Header name a caller would (wrongly) use. Default `x-nodii-idempotency-key`. */
|
|
220
|
+
headerName?: string;
|
|
221
|
+
/** TTL (seconds) for the saga claim entry. Defaults to library config. */
|
|
222
|
+
ttlSeconds?: number;
|
|
223
|
+
}
|
|
189
224
|
/** Deadline guard config — spec § 5.10. */
|
|
190
225
|
export interface DeadlineConfig {
|
|
191
226
|
/** Budget reserved at the inbound boundary in ms. Default 50ms per § 5.10. */
|
|
@@ -259,6 +294,27 @@ export interface StandardServerStackConfig {
|
|
|
259
294
|
requireSagaContextMethods?: ReadonlySet<string>;
|
|
260
295
|
/** sagaContext enforcement mode: 'warn' (default) | 'reject'. */
|
|
261
296
|
sagaContextEnforce?: "warn" | "reject";
|
|
297
|
+
/**
|
|
298
|
+
* OPT-IN MFA leg (D423 / request 388ea83b). When set, wires
|
|
299
|
+
* `withMfaRequiredUnary` at the locked MFA slot — methods the policy marks
|
|
300
|
+
* MFA-required are rejected without fresh proven MFA. Omitted = MFA slot is
|
|
301
|
+
* a pure passthrough (back-compat).
|
|
302
|
+
*/
|
|
303
|
+
mfa?: MfaRequiredConfig;
|
|
304
|
+
/**
|
|
305
|
+
* OPT-IN saga-envelope leg (D423 / request 388ea83b). When `true`, wires
|
|
306
|
+
* `withSagaEnvelopeUnary` so the full 7-header `x-saga-*` envelope is parsed
|
|
307
|
+
* onto `aug.sagaEnvelope`. Required for the saga-idempotency leg. Omitted =
|
|
308
|
+
* no envelope parsing (back-compat: only the generic `sagaContext` runs).
|
|
309
|
+
*/
|
|
310
|
+
sagaEnvelope?: boolean;
|
|
311
|
+
/**
|
|
312
|
+
* OPT-IN saga-aware idempotency leg (D423 / request 388ea83b). When set,
|
|
313
|
+
* wires `withSagaIdempotencyUnary` — a canonical `(tenant_id, method,
|
|
314
|
+
* saga_id, step_id)` key over the service idempotency store for saga calls,
|
|
315
|
+
* rejecting a caller idempotency-key. Requires `sagaEnvelope: true`.
|
|
316
|
+
*/
|
|
317
|
+
sagaIdempotency?: SagaIdempotencyLegConfig | boolean;
|
|
262
318
|
}
|
|
263
319
|
/** Subset of `@nodii/telemetry` we depend on at runtime. */
|
|
264
320
|
export interface TelemetryShim {
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AASA;;;;;;;GAOG;AACH,MAAM,WAAW,SAAS;IACxB,0EAA0E;IAC1E,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,QAAQ,EAAE;QACR,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,OAAO,EAAE,CAAC;KACtC,CAAC;IACF,OAAO,EAAE,OAAO,CAAC;IACjB;;;;OAIG;IACH,EAAE,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;IACvD,2DAA2D;IAC3D,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,sCAAsC;IACtC,YAAY,CAAC,EAAE;QACb,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;KAC5B,CAAC;IACF,yDAAyD;IACzD,SAAS,CAAC,EAAE;QACV,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;IACF;;;;;;OAMG;IACH,cAAc,CAAC,EAAE;QACf,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,oDAAoD;AACpD,EAAE;AACF,gFAAgF;AAChF,EAAE;AACF,2EAA2E;AAC3E,2EAA2E;AAC3E,2EAA2E;AAC3E,wEAAwE;
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,oDAAoD;AACpD,EAAE;AACF,gFAAgF;AAChF,EAAE;AACF,2EAA2E;AAC3E,2EAA2E;AAC3E,2EAA2E;AAC3E,wEAAwE;AAyHxE,gFAAgF;AAChF,MAAM,OAAO,eAAgB,SAAQ,KAAK;IAC/B,IAAI,CAAiB;IACrB,MAAM,CAAS;IACxB,YAAY,IAAoB,EAAE,MAAc,EAAE,KAAe;QAC/D,KAAK,CAAC,GAAG,IAAI,KAAK,MAAM,EAAE,CAAC,CAAC;QAC5B,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;QAC9B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,KAAK,KAAK,SAAS;YAAG,IAA4B,CAAC,KAAK,GAAG,KAAK,CAAC;IACvE,CAAC;CACF;AAED,8EAA8E;AAC9E,MAAM,OAAO,iBAAkB,SAAQ,eAAe;IACpD,YAAY,MAAM,GAAG,mBAAmB;QACtC,KAAK,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QAC3B,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IAClC,CAAC;CACF;AAED,4DAA4D;AAC5D,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IAC5C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;IACpC,CAAC;CACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nodii/grpc-interceptors",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Substrate gRPC interceptor library for the Nodii microservice stack — 8 cross-cutting interceptors (logging, audit, enrichAuthContext, tenantContext, auditContext, deadlineGuard, cancellationGuard, errorMap) + re-export façade over @nodii/grpc-auth/saga/idempotency + locked-order createStandardServerStack factory. Spec: planning hub docKey=grpc-interceptors.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -23,15 +23,18 @@
|
|
|
23
23
|
"typecheck": "tsc --noEmit",
|
|
24
24
|
"test": "bun test"
|
|
25
25
|
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"jose": "^5.9.6"
|
|
28
|
+
},
|
|
26
29
|
"devDependencies": {
|
|
27
|
-
"@nodii/audit-chain": "0.
|
|
30
|
+
"@nodii/audit-chain": "0.11.0",
|
|
28
31
|
"ioredis": "^5.4.0",
|
|
29
32
|
"postgres": "^3.4.0",
|
|
30
33
|
"typescript": "^5.9.3",
|
|
31
|
-
"@nodii/grpc-auth": "0.
|
|
32
|
-
"@nodii/idempotency": "0.
|
|
33
|
-
"@nodii/saga": "0.
|
|
34
|
-
"@nodii/telemetry": "0.
|
|
34
|
+
"@nodii/grpc-auth": "0.11.0",
|
|
35
|
+
"@nodii/idempotency": "0.7.0",
|
|
36
|
+
"@nodii/saga": "0.9.0",
|
|
37
|
+
"@nodii/telemetry": "0.16.0"
|
|
35
38
|
},
|
|
36
39
|
"repository": {
|
|
37
40
|
"type": "git",
|