@astrasyncai/verification-gateway 3.4.0 → 3.6.0
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 +102 -0
- package/dist/adapter-interface/interface.d.mts +2 -2
- package/dist/adapter-interface/interface.d.ts +2 -2
- package/dist/adapters/express.d.mts +2 -2
- package/dist/adapters/express.d.ts +2 -2
- package/dist/adapters/express.js +113 -20
- package/dist/adapters/express.js.map +1 -1
- package/dist/adapters/express.mjs +113 -20
- package/dist/adapters/express.mjs.map +1 -1
- package/dist/adapters/mcp.d.mts +1 -1
- package/dist/adapters/mcp.d.ts +1 -1
- package/dist/adapters/mcp.js +96 -20
- package/dist/adapters/mcp.js.map +1 -1
- package/dist/adapters/mcp.mjs +96 -20
- package/dist/adapters/mcp.mjs.map +1 -1
- package/dist/adapters/nextjs.d.mts +2 -2
- package/dist/adapters/nextjs.d.ts +2 -2
- package/dist/adapters/nextjs.js +101 -20
- package/dist/adapters/nextjs.js.map +1 -1
- package/dist/adapters/nextjs.mjs +101 -20
- package/dist/adapters/nextjs.mjs.map +1 -1
- package/dist/adapters/sdk.d.mts +2 -2
- package/dist/adapters/sdk.d.ts +2 -2
- package/dist/adapters/sdk.js +19 -16
- package/dist/adapters/sdk.js.map +1 -1
- package/dist/adapters/sdk.mjs +19 -16
- package/dist/adapters/sdk.mjs.map +1 -1
- package/dist/agent/index.d.mts +2 -2
- package/dist/agent/index.d.ts +2 -2
- package/dist/browser/background.js +22 -16
- package/dist/browser/background.js.map +1 -1
- package/dist/browser/background.mjs +22 -16
- package/dist/browser/background.mjs.map +1 -1
- package/dist/browser/browser-adapter.d.mts +2 -2
- package/dist/browser/browser-adapter.d.ts +2 -2
- package/dist/cli/index.d.mts +2 -2
- package/dist/cli/index.d.ts +2 -2
- package/dist/cursor/cursor-adapter.d.mts +2 -2
- package/dist/cursor/cursor-adapter.d.ts +2 -2
- package/dist/cursor/extension.d.mts +2 -2
- package/dist/cursor/extension.d.ts +2 -2
- package/dist/cursor/extension.js +22 -16
- package/dist/cursor/extension.js.map +1 -1
- package/dist/cursor/extension.mjs +22 -16
- package/dist/cursor/extension.mjs.map +1 -1
- package/dist/{express-CeFvNWDD.d.mts → express-BnMO-H3y.d.mts} +1 -1
- package/dist/{express-RIh05wW2.d.ts → express-DQhDzNAt.d.ts} +1 -1
- package/dist/gateway/gateway.d.mts +2 -2
- package/dist/gateway/gateway.d.ts +2 -2
- package/dist/gateway/gateway.js +22 -16
- package/dist/gateway/gateway.js.map +1 -1
- package/dist/gateway/gateway.mjs +22 -16
- package/dist/gateway/gateway.mjs.map +1 -1
- package/dist/git-trigger/git-hooks.d.mts +2 -2
- package/dist/git-trigger/git-hooks.d.ts +2 -2
- package/dist/{index-DgVBEiwI.d.mts → index-BT_aRn-I.d.mts} +1 -1
- package/dist/{index-BYu1FKMy.d.ts → index-D8-dvUFA.d.ts} +1 -1
- package/dist/{index-Bw4FeHMY.d.mts → index-WHKq2v17.d.mts} +5 -1
- package/dist/{index-C-K0WCrk.d.ts → index-rftma0ln.d.ts} +5 -1
- package/dist/index.d.mts +55 -9
- package/dist/index.d.ts +55 -9
- package/dist/index.js +184 -34
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +182 -34
- package/dist/index.mjs.map +1 -1
- package/dist/local-evaluator/evaluator.d.mts +2 -2
- package/dist/local-evaluator/evaluator.d.ts +2 -2
- package/dist/{nextjs-xciaMpsZ.d.mts → nextjs-DFKYhxe6.d.mts} +1 -1
- package/dist/{nextjs--xR9zbSJ.d.ts → nextjs-DPj7ZNlq.d.ts} +1 -1
- package/dist/{sdk-FP7xT9F8.d.ts → sdk-CAcHgB5x.d.ts} +1 -1
- package/dist/{sdk-CJYCLMI0.d.mts → sdk-Cy3wAmDt.d.mts} +1 -1
- package/dist/transport/index.d.mts +2 -2
- package/dist/transport/index.d.ts +2 -2
- package/dist/transport/index.js +22 -4
- package/dist/transport/index.js.map +1 -1
- package/dist/transport/index.mjs +22 -4
- package/dist/transport/index.mjs.map +1 -1
- package/dist/{types-Cj92yKeA.d.mts → types-B-ubjHER.d.mts} +1 -1
- package/dist/{types-DOeL-dIw.d.ts → types-Bgn5gxHQ.d.ts} +1 -1
- package/dist/{types-Cwrvo30g.d.mts → types-CKe8O0lR.d.mts} +65 -3
- package/dist/{types-Cwrvo30g.d.ts → types-CKe8O0lR.d.ts} +65 -3
- package/dist/ui/index.d.mts +1 -1
- package/dist/ui/index.d.ts +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -409,6 +409,61 @@ interface VerificationResult {
|
|
|
409
409
|
}
|
|
410
410
|
```
|
|
411
411
|
|
|
412
|
+
## Settlement Authorization
|
|
413
|
+
|
|
414
|
+
For direct-path merchants settling a priced cart, call `authorizeSettlement()` **after** pricing — the middleware only verifies identity/access, not the transaction value:
|
|
415
|
+
|
|
416
|
+
```typescript
|
|
417
|
+
import { authorizeSettlement } from '@astrasyncai/verification-gateway';
|
|
418
|
+
|
|
419
|
+
const decision = await authorizeSettlement(config, {
|
|
420
|
+
agentId: req.agentVerification.agent.astraId,
|
|
421
|
+
value: cart.total, // YOUR authoritative priced total, never agent-supplied
|
|
422
|
+
currency: 'USD',
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
if (!decision.authorized) {
|
|
426
|
+
// decision.stepUpApproval?.pollUrl — if in the approval band, the owner can approve
|
|
427
|
+
return res.status(402).json({ error: decision.reason, stepUpApproval: decision.stepUpApproval });
|
|
428
|
+
}
|
|
429
|
+
// Safe to settle
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
## Step-Up Approval
|
|
433
|
+
|
|
434
|
+
When a transaction value is between the agent's Autonomous Limit and Hard Limit, verify-access returns `stepUpApproval` on the result:
|
|
435
|
+
|
|
436
|
+
```typescript
|
|
437
|
+
interface StepUpApprovalInfo {
|
|
438
|
+
approvalId: string; // Capability token (UUID)
|
|
439
|
+
pollUrl: string; // GET /api/step-up-approvals/poll/:approvalId
|
|
440
|
+
expiresAt: string; // ISO-8601, 5-minute TTL
|
|
441
|
+
}
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
Poll the `pollUrl` (unauthenticated, rate-limited 60 req/min) to check if the owner approved. The `getApprovalPollingInfo(result)` helper extracts it from a `VerificationResult`.
|
|
445
|
+
|
|
446
|
+
## Settlement Artifacts
|
|
447
|
+
|
|
448
|
+
On a clean merchant-mediated grant where the owner has a verified payment instrument, verify-access returns a `settlement` object:
|
|
449
|
+
|
|
450
|
+
```typescript
|
|
451
|
+
interface SettlementArtifact {
|
|
452
|
+
type: string; // e.g. "stablecoin_voucher"
|
|
453
|
+
artifact: string; // JWS compact-serialised (ES256)
|
|
454
|
+
binding: {
|
|
455
|
+
merchantId: string;
|
|
456
|
+
amount: number;
|
|
457
|
+
currency: string;
|
|
458
|
+
sessionId: string;
|
|
459
|
+
singleUse: true;
|
|
460
|
+
expiresAt: string;
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
Verify vouchers server-side via `POST /api/wallets/voucher/verify` or fetch the signing keys from `GET /api/wallets/voucher/.well-known/jwks.json`.
|
|
466
|
+
|
|
412
467
|
## Configuration
|
|
413
468
|
|
|
414
469
|
```typescript
|
|
@@ -430,6 +485,22 @@ interface GatewayConfig {
|
|
|
430
485
|
// forwarded on every verify-access call so the server attributes traffic
|
|
431
486
|
// directly to this endpoint rather than resolving by URL.
|
|
432
487
|
|
|
488
|
+
// Step-up hold-and-poll (v3.6.0+, OPT-IN). When stepUpMaxWaitMs is set, the
|
|
489
|
+
// express/mcp/nextjs middleware HOLDS a step_up_required request, polls the
|
|
490
|
+
// approval, and on 'approved' re-verifies once (cache-bypassed) so the
|
|
491
|
+
// backend redeems the single-use approval — the owner approves in their
|
|
492
|
+
// dashboard while the original request waits (MFA model). Unset = current
|
|
493
|
+
// fail-closed behavior (immediate 403 with stepUpApproval polling info).
|
|
494
|
+
// 0 → hold until the approval's expiresAt (+5s grace; 5-min fallback)
|
|
495
|
+
// >0 → hold at most this many milliseconds
|
|
496
|
+
// CAUTION: Next.js edge runtimes enforce ~25s wall-clock budgets — use a
|
|
497
|
+
// small value there (≤20000) or gate in a Node route handler instead.
|
|
498
|
+
stepUpMaxWaitMs?: number;
|
|
499
|
+
// Poll interval for the hold (default 3000ms, 250ms floor). The poll route
|
|
500
|
+
// is rate-limited 60/min/IP: at the default interval ~3 concurrent holds
|
|
501
|
+
// per merchant IP fit; excess polls degrade the hold to 'timeout'.
|
|
502
|
+
stepUpPollIntervalMs?: number;
|
|
503
|
+
|
|
433
504
|
// Init self-test (v2.2.3+) — fires a HEAD probe to verify-access on first
|
|
434
505
|
// call and warns if apiBaseUrl is pointing at HTML (catches the marketing-
|
|
435
506
|
// 404 case). Set true for tests where the extra request is undesirable.
|
|
@@ -494,6 +565,37 @@ Pre-v2.4.2 used the value `pass-through` — renamed in v2.4.2 to disambiguate "
|
|
|
494
565
|
|
|
495
566
|
## Changelog
|
|
496
567
|
|
|
568
|
+
### v3.4.0 — Type alignment for LMAX settlement + step-up approval
|
|
569
|
+
|
|
570
|
+
- `StepUpApprovalInfo` and `SettlementArtifact` interfaces added to `VerificationResult`
|
|
571
|
+
- `stepUpApproval` surfaced in Express/MCP/Next.js adapter deny response bodies
|
|
572
|
+
- `SettlementDecision` carries `stepUpApproval` on step-up denials via `authorizeSettlement()`
|
|
573
|
+
- `getApprovalPollingInfo()` simplified — typed field, no more `unknown` casts
|
|
574
|
+
- `Attestation.checkedAt` (required) — merchant freshness gate timestamp
|
|
575
|
+
- `VERSION` constant updated to `3.4.0`
|
|
576
|
+
|
|
577
|
+
### v3.3.0 — Direct-path value enforcement
|
|
578
|
+
|
|
579
|
+
- `authorizeSettlement(config, { agentId, value, currency })` — fail-closed settlement gate for direct-path merchants
|
|
580
|
+
- `restrictions` surfaces `approvalThreshold` (enforced per-tx) and `maxPerPeriod` (not yet enforced)
|
|
581
|
+
- ASCII-safe agent-facing strings
|
|
582
|
+
|
|
583
|
+
### v3.2.1 — Platform-agent go-live readiness
|
|
584
|
+
|
|
585
|
+
- Canonical PDLSS limits terminology (`autonomousThreshold`, `approvalThreshold`)
|
|
586
|
+
- Step-up/approval fail-closed in Express/MCP/Next.js adapters via `approval-gate.ts`
|
|
587
|
+
|
|
588
|
+
### v3.2.0 — Commerce observability
|
|
589
|
+
|
|
590
|
+
- Access-level band no longer gates (informational only); `requiresStepUp` carries the signal
|
|
591
|
+
- Trust score redacted from agent-facing responses
|
|
592
|
+
- Cross-merchant cache key fix
|
|
593
|
+
|
|
594
|
+
### v3.1.0 — Canonical PDLSS vocabulary (Bug 14)
|
|
595
|
+
|
|
596
|
+
- Two-axis purpose/action chains with dotted action tokens
|
|
597
|
+
- Route send-mapping for tool→semantic-action translation
|
|
598
|
+
|
|
497
599
|
### v2.4.6 — Round-14 partner integration testing
|
|
498
600
|
|
|
499
601
|
**⚠️ BREAKING CHANGE — `endpointUrl` → `counterpartyUrl` on `POST /api/endpoints` AND `PUT /api/endpoints/{id}`**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AstraSyncGateway } from '../gateway/gateway.mjs';
|
|
2
|
-
import { A as AgentAction, I as InterceptResult, P as PDLSSContext, V as VerificationDecision } from '../types-
|
|
3
|
-
import '../types-
|
|
2
|
+
import { A as AgentAction, I as InterceptResult, P as PDLSSContext, V as VerificationDecision } from '../types-B-ubjHER.mjs';
|
|
3
|
+
import '../types-CKe8O0lR.mjs';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* PlatformAdapter Interface
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AstraSyncGateway } from '../gateway/gateway.js';
|
|
2
|
-
import { A as AgentAction, I as InterceptResult, P as PDLSSContext, V as VerificationDecision } from '../types-
|
|
3
|
-
import '../types-
|
|
2
|
+
import { A as AgentAction, I as InterceptResult, P as PDLSSContext, V as VerificationDecision } from '../types-Bgn5gxHQ.js';
|
|
3
|
+
import '../types-CKe8O0lR.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* PlatformAdapter Interface
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import 'express';
|
|
2
|
-
import '../types-
|
|
3
|
-
export { c as createMiddleware, a as extractAstraSyncCredentials } from '../express-
|
|
2
|
+
import '../types-CKe8O0lR.mjs';
|
|
3
|
+
export { c as createMiddleware, a as extractAstraSyncCredentials } from '../express-BnMO-H3y.mjs';
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import 'express';
|
|
2
|
-
import '../types-
|
|
3
|
-
export { c as createMiddleware, a as extractAstraSyncCredentials } from '../express-
|
|
2
|
+
import '../types-CKe8O0lR.js';
|
|
3
|
+
export { c as createMiddleware, a as extractAstraSyncCredentials } from '../express-DQhDzNAt.js';
|
package/dist/adapters/express.js
CHANGED
|
@@ -25,6 +25,64 @@ __export(express_exports, {
|
|
|
25
25
|
});
|
|
26
26
|
module.exports = __toCommonJS(express_exports);
|
|
27
27
|
|
|
28
|
+
// src/adapters/stepup-poll.ts
|
|
29
|
+
var DEFAULT_POLL_INTERVAL_MS = 3e3;
|
|
30
|
+
var MIN_POLL_INTERVAL_MS = 250;
|
|
31
|
+
var FALLBACK_MAX_WAIT_MS = 5 * 60 * 1e3;
|
|
32
|
+
var EXPIRY_GRACE_MS = 5e3;
|
|
33
|
+
var POLL_REQUEST_TIMEOUT_MS = 1e4;
|
|
34
|
+
function resolvePollUrl(config, pollUrl) {
|
|
35
|
+
if (pollUrl.startsWith("http")) return pollUrl;
|
|
36
|
+
const origin = (config.apiBaseUrl ?? "").replace(/\/api\/?$/, "");
|
|
37
|
+
return origin + pollUrl;
|
|
38
|
+
}
|
|
39
|
+
async function pollStepUpStatus(config, approval) {
|
|
40
|
+
if (!approval.pollUrl) return null;
|
|
41
|
+
try {
|
|
42
|
+
const res = await fetch(resolvePollUrl(config, approval.pollUrl), {
|
|
43
|
+
method: "GET",
|
|
44
|
+
signal: AbortSignal.timeout(POLL_REQUEST_TIMEOUT_MS)
|
|
45
|
+
});
|
|
46
|
+
if (!res.ok) return null;
|
|
47
|
+
const body = await res.json();
|
|
48
|
+
const data = body?.data;
|
|
49
|
+
const status = data && typeof data === "object" ? data.status : body?.status;
|
|
50
|
+
return typeof status === "string" ? status : null;
|
|
51
|
+
} catch {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function resolveDeadline(config, approval) {
|
|
56
|
+
const now = Date.now();
|
|
57
|
+
const maxWait = config.stepUpMaxWaitMs ?? 0;
|
|
58
|
+
if (maxWait > 0) return now + maxWait;
|
|
59
|
+
if (approval.expiresAt) {
|
|
60
|
+
const expiry = Date.parse(approval.expiresAt);
|
|
61
|
+
if (!Number.isNaN(expiry)) return Math.max(now, expiry) + EXPIRY_GRACE_MS;
|
|
62
|
+
}
|
|
63
|
+
return now + FALLBACK_MAX_WAIT_MS;
|
|
64
|
+
}
|
|
65
|
+
function sleep(ms) {
|
|
66
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
67
|
+
}
|
|
68
|
+
async function awaitStepUpApproval(config, approval) {
|
|
69
|
+
const deadline = resolveDeadline(config, approval);
|
|
70
|
+
const pollInterval = Math.max(
|
|
71
|
+
MIN_POLL_INTERVAL_MS,
|
|
72
|
+
config.stepUpPollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS
|
|
73
|
+
);
|
|
74
|
+
while (Date.now() < deadline) {
|
|
75
|
+
const status = await pollStepUpStatus(config, approval);
|
|
76
|
+
if (status && status !== "pending") {
|
|
77
|
+
return status;
|
|
78
|
+
}
|
|
79
|
+
const remaining = deadline - Date.now();
|
|
80
|
+
if (remaining <= 0) break;
|
|
81
|
+
await sleep(Math.min(pollInterval, remaining));
|
|
82
|
+
}
|
|
83
|
+
return "timeout";
|
|
84
|
+
}
|
|
85
|
+
|
|
28
86
|
// src/access-levels.ts
|
|
29
87
|
function getTrustLevel(score) {
|
|
30
88
|
if (score >= 80) return "PLATINUM";
|
|
@@ -34,7 +92,7 @@ function getTrustLevel(score) {
|
|
|
34
92
|
}
|
|
35
93
|
|
|
36
94
|
// src/version.ts
|
|
37
|
-
var SDK_VERSION = "3.
|
|
95
|
+
var SDK_VERSION = "3.6.0";
|
|
38
96
|
|
|
39
97
|
// src/well-known.ts
|
|
40
98
|
var CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
@@ -104,10 +162,10 @@ var DEFAULT_CONFIG = {
|
|
|
104
162
|
// through (`hasMinimumAccess('guidance', 'guidance') === true`).
|
|
105
163
|
defaultAccessLevel: "none",
|
|
106
164
|
// minTrustScore + minTrustScoreForFull deprecated in v2.3.0 — server decides.
|
|
107
|
-
//
|
|
108
|
-
//
|
|
109
|
-
//
|
|
110
|
-
// Set cacheTtl: 0 to disable caching entirely.
|
|
165
|
+
// cacheTtl deliberately unset. When undefined, cacheResult applies the 60s
|
|
166
|
+
// default; the caller's explicit value is honoured for cacheable results.
|
|
167
|
+
// Denials and step-up escalations are NEVER cached (3.6.0) regardless of
|
|
168
|
+
// cacheTtl. Set cacheTtl: 0 to disable caching entirely.
|
|
111
169
|
debug: false
|
|
112
170
|
};
|
|
113
171
|
var initCheckPerformed = false;
|
|
@@ -194,10 +252,9 @@ function getCachedResult(request, counterpartyId) {
|
|
|
194
252
|
}
|
|
195
253
|
return null;
|
|
196
254
|
}
|
|
197
|
-
var
|
|
198
|
-
var DEFAULT_STEP_UP_TTL_SECONDS = 300;
|
|
255
|
+
var DEFAULT_CACHE_TTL_SECONDS = 60;
|
|
199
256
|
function cacheResult(request, result, configuredTtl, counterpartyId) {
|
|
200
|
-
const ttlSeconds = configuredTtl && configuredTtl > 0 ? configuredTtl :
|
|
257
|
+
const ttlSeconds = configuredTtl && configuredTtl > 0 ? configuredTtl : DEFAULT_CACHE_TTL_SECONDS;
|
|
201
258
|
const key = getCacheKey(request, counterpartyId);
|
|
202
259
|
verificationCache.set(key, {
|
|
203
260
|
result,
|
|
@@ -373,7 +430,7 @@ async function callVerifyAccessAPI(config, request) {
|
|
|
373
430
|
};
|
|
374
431
|
}
|
|
375
432
|
}
|
|
376
|
-
async function verify(config, request) {
|
|
433
|
+
async function verify(config, request, options) {
|
|
377
434
|
const mergedConfig = { ...DEFAULT_CONFIG, ...config };
|
|
378
435
|
const urls = mergedConfig.apiBaseUrl ? getCachedWellKnownUrls(mergedConfig.apiBaseUrl) : void 0;
|
|
379
436
|
if (!initCheckPerformed && !mergedConfig.disableInitChecks && mergedConfig.apiBaseUrl) {
|
|
@@ -389,7 +446,7 @@ async function verify(config, request) {
|
|
|
389
446
|
"[VerificationGateway] minTrustScore / minTrustScoreForFull are deprecated in v2.3.0 and have no effect. Server is now the single source of truth for access-level decisions (the SDK reads access.accessLevel from the verify-access response). To gate access to an endpoint, configure the endpoint's trust_score_requirement server-side."
|
|
390
447
|
);
|
|
391
448
|
}
|
|
392
|
-
if (mergedConfig.cacheTtl !== 0) {
|
|
449
|
+
if (mergedConfig.cacheTtl !== 0 && !options?.bypassCache) {
|
|
393
450
|
const cached = getCachedResult(request, mergedConfig.counterpartyId);
|
|
394
451
|
if (cached) {
|
|
395
452
|
if (mergedConfig.debug) {
|
|
@@ -409,6 +466,8 @@ async function verify(config, request) {
|
|
|
409
466
|
console.log("[VerificationGateway] Calling verify-access API");
|
|
410
467
|
}
|
|
411
468
|
const apiResponse = await callVerifyAccessAPI(mergedConfig, enrichedRequest);
|
|
469
|
+
const rawServerDenialReasons = apiResponse.denialReasons;
|
|
470
|
+
const serverDenialReasons = Array.isArray(rawServerDenialReasons) && rawServerDenialReasons.length > 0 ? rawServerDenialReasons : void 0;
|
|
412
471
|
if (!apiResponse.success) {
|
|
413
472
|
return createGuidanceResponse(mergedConfig, apiResponse.error, {
|
|
414
473
|
source: "api_error",
|
|
@@ -430,7 +489,10 @@ async function verify(config, request) {
|
|
|
430
489
|
// ALSO short-circuit on `!identityVerified || !policyAllowed` before
|
|
431
490
|
// the gate check — belt-and-braces.
|
|
432
491
|
accessLevel: "none",
|
|
433
|
-
|
|
492
|
+
// 3.6.0: the server now emits top-level denialReasons on every deny
|
|
493
|
+
// path — prefer it verbatim; the failure-message reconstruction remains
|
|
494
|
+
// as the fallback for older backends.
|
|
495
|
+
denialReasons: serverDenialReasons ?? (aggregatedFailures && aggregatedFailures.length > 0 ? aggregatedFailures.map((f) => f.message) : apiResponse.access?.reason ? [apiResponse.access.reason] : ["Access denied"]),
|
|
434
496
|
failures: aggregatedFailures,
|
|
435
497
|
requiresStepUp: apiResponse.access?.requiresStepUp,
|
|
436
498
|
requiresApproval: apiResponse.access?.requiresApproval,
|
|
@@ -507,9 +569,7 @@ async function verify(config, request) {
|
|
|
507
569
|
if (result.recommendation === "deny") {
|
|
508
570
|
result.policyAllowed = false;
|
|
509
571
|
result.accessLevel = "none";
|
|
510
|
-
result.denialReasons = result.recommendationReasons
|
|
511
|
-
"Access denied by AstraSync recommendation"
|
|
512
|
-
];
|
|
572
|
+
result.denialReasons = serverDenialReasons ?? result.recommendationReasons ?? ["Access denied by AstraSync recommendation"];
|
|
513
573
|
result.guidance = result.runtimeChallenge ? {
|
|
514
574
|
message: `Verification failed: ${result.runtimeChallenge.reason || "runtime challenge failed"}`,
|
|
515
575
|
registrationUrl: urls?.registrationUrl ?? "",
|
|
@@ -521,9 +581,10 @@ async function verify(config, request) {
|
|
|
521
581
|
};
|
|
522
582
|
} else if (result.recommendation === "step_up_required") {
|
|
523
583
|
result.requiresStepUp = true;
|
|
524
|
-
result.denialReasons = result.recommendationReasons
|
|
584
|
+
result.denialReasons = serverDenialReasons ?? result.recommendationReasons ?? ["Step-up verification required"];
|
|
525
585
|
}
|
|
526
|
-
|
|
586
|
+
const escalates = result.requiresStepUp === true || result.requiresApproval === true || result.recommendation === "step_up_required";
|
|
587
|
+
if (mergedConfig.cacheTtl !== 0 && result.recommendation !== "deny" && !escalates) {
|
|
527
588
|
cacheResult(request, result, mergedConfig.cacheTtl, mergedConfig.counterpartyId);
|
|
528
589
|
}
|
|
529
590
|
return result;
|
|
@@ -836,7 +897,10 @@ function defaultOnDenied(result, _req, res) {
|
|
|
836
897
|
// Round-10: aggregated per-dimension detail + correlation handle.
|
|
837
898
|
failures: result.failures,
|
|
838
899
|
correlationId: result.correlationId,
|
|
839
|
-
stepUpApproval: result.stepUpApproval
|
|
900
|
+
stepUpApproval: result.stepUpApproval,
|
|
901
|
+
// 3.6.0: terminal outcome of an opt-in hold-and-poll wait (denied /
|
|
902
|
+
// expired / consumed / timeout). Absent when the feature is off.
|
|
903
|
+
...result.stepUpOutcome && { stepUpOutcome: result.stepUpOutcome }
|
|
840
904
|
}
|
|
841
905
|
});
|
|
842
906
|
}
|
|
@@ -986,7 +1050,22 @@ function createMiddleware(options) {
|
|
|
986
1050
|
);
|
|
987
1051
|
}
|
|
988
1052
|
const agentCardUrl = typeof req.headers["x-astrasync-agent-card"] === "string" ? req.headers["x-astrasync-agent-card"] : void 0;
|
|
989
|
-
|
|
1053
|
+
let txValue;
|
|
1054
|
+
let txCurrency;
|
|
1055
|
+
if (config.extractTransactionValue) {
|
|
1056
|
+
try {
|
|
1057
|
+
const extracted = config.extractTransactionValue(req);
|
|
1058
|
+
if (extracted) {
|
|
1059
|
+
txValue = extracted.value;
|
|
1060
|
+
txCurrency = extracted.currency;
|
|
1061
|
+
}
|
|
1062
|
+
} catch (e) {
|
|
1063
|
+
if (config.debug) {
|
|
1064
|
+
console.warn("[VerificationGateway] extractTransactionValue threw:", e);
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
const verificationRequest = {
|
|
990
1069
|
credentials,
|
|
991
1070
|
purpose,
|
|
992
1071
|
action: pdlssPair.action,
|
|
@@ -996,6 +1075,8 @@ function createMiddleware(options) {
|
|
|
996
1075
|
counterpartyType: config.counterpartyType || "api",
|
|
997
1076
|
enableRuntimeChallenge,
|
|
998
1077
|
durationRequired: astraCreds?.pdlss?.duration?.maxSessionDuration,
|
|
1078
|
+
...txValue !== void 0 && { transactionValue: txValue },
|
|
1079
|
+
...txCurrency && { currency: txCurrency },
|
|
999
1080
|
callerMetadata: {
|
|
1000
1081
|
sourceIp: originalClientIp,
|
|
1001
1082
|
userAgent: req.headers["user-agent"],
|
|
@@ -1004,9 +1085,21 @@ function createMiddleware(options) {
|
|
|
1004
1085
|
forwardedFor: forwardedForStr,
|
|
1005
1086
|
agentCardUrl
|
|
1006
1087
|
}
|
|
1007
|
-
}
|
|
1088
|
+
};
|
|
1089
|
+
let result = await verify(config, verificationRequest);
|
|
1008
1090
|
req.agentVerification = result;
|
|
1009
|
-
|
|
1091
|
+
let sessionId = result.sessionId;
|
|
1092
|
+
if (config.stepUpMaxWaitMs !== void 0 && result.identityVerified && requiresHumanApproval(result) && result.stepUpApproval?.pollUrl) {
|
|
1093
|
+
const outcome = await awaitStepUpApproval(config, result.stepUpApproval);
|
|
1094
|
+
res.setHeader("X-Astra-Step-Up", outcome);
|
|
1095
|
+
if (outcome === "approved") {
|
|
1096
|
+
result = await verify(config, verificationRequest, { bypassCache: true });
|
|
1097
|
+
req.agentVerification = result;
|
|
1098
|
+
sessionId = result.sessionId;
|
|
1099
|
+
} else {
|
|
1100
|
+
result.stepUpOutcome = outcome;
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1010
1103
|
if (!result.identityVerified || !result.policyAllowed) {
|
|
1011
1104
|
if (shouldRecordDecisions && sessionId) {
|
|
1012
1105
|
recordDecision(config, sessionId, "denied", result.denialReasons?.[0]).catch(() => {
|