@astrasyncai/verification-gateway 5.17.0 → 5.17.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/README.md +94 -18
- package/dist/adapters/express.d.mts +1 -1
- package/dist/adapters/express.d.ts +1 -1
- package/dist/adapters/express.js +33 -5
- package/dist/adapters/express.js.map +1 -1
- package/dist/adapters/express.mjs +33 -5
- 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 +24 -5
- package/dist/adapters/mcp.js.map +1 -1
- package/dist/adapters/mcp.mjs +24 -5
- package/dist/adapters/mcp.mjs.map +1 -1
- package/dist/adapters/nextjs.d.mts +1 -1
- package/dist/adapters/nextjs.d.ts +1 -1
- package/dist/adapters/nextjs.js +24 -5
- package/dist/adapters/nextjs.js.map +1 -1
- package/dist/adapters/nextjs.mjs +24 -5
- package/dist/adapters/nextjs.mjs.map +1 -1
- package/dist/adapters/sdk.d.mts +1 -1
- package/dist/adapters/sdk.d.ts +1 -1
- package/dist/adapters/sdk.js +5 -1
- package/dist/adapters/sdk.js.map +1 -1
- package/dist/adapters/sdk.mjs +5 -1
- package/dist/adapters/sdk.mjs.map +1 -1
- package/dist/agent/index.js +1 -1
- package/dist/agent/index.js.map +1 -1
- package/dist/agent/index.mjs +1 -1
- package/dist/agent/index.mjs.map +1 -1
- package/dist/bin/astrasync-claude-hook.js +5 -1
- package/dist/bin/astrasync-codex-hook.js +5 -1
- package/dist/bin/astrasync-guard.js +32 -7
- package/dist/bin/astrasync.js +32 -7
- package/dist/browser/background.js +5 -1
- package/dist/browser/background.js.map +1 -1
- package/dist/browser/background.mjs +5 -1
- package/dist/browser/background.mjs.map +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +1 -1
- package/dist/cli/index.mjs.map +1 -1
- package/dist/codex/index.js +5 -1
- package/dist/codex/index.js.map +1 -1
- package/dist/codex/index.mjs +5 -1
- package/dist/codex/index.mjs.map +1 -1
- package/dist/cursor/extension.js +5 -1
- package/dist/cursor/extension.js.map +1 -1
- package/dist/cursor/extension.mjs +5 -1
- package/dist/cursor/extension.mjs.map +1 -1
- package/dist/edge-config.d.mts +1 -1
- package/dist/edge-config.d.ts +1 -1
- package/dist/edge-config.js +1 -1
- package/dist/edge-config.js.map +1 -1
- package/dist/edge-config.mjs +1 -1
- package/dist/edge-config.mjs.map +1 -1
- package/dist/edge-core/index.d.mts +1 -1
- package/dist/edge-core/index.d.ts +1 -1
- package/dist/edge-core/index.js +5 -1
- package/dist/edge-core/index.js.map +1 -1
- package/dist/edge-core/index.mjs +5 -1
- package/dist/edge-core/index.mjs.map +1 -1
- package/dist/gateway/gateway.js +5 -1
- package/dist/gateway/gateway.js.map +1 -1
- package/dist/gateway/gateway.mjs +5 -1
- package/dist/gateway/gateway.mjs.map +1 -1
- package/dist/git-trigger/git-hooks.d.mts +1 -1
- package/dist/git-trigger/git-hooks.d.ts +1 -1
- package/dist/index.d.mts +16 -2
- package/dist/index.d.ts +16 -2
- package/dist/index.js +33 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +33 -5
- package/dist/index.mjs.map +1 -1
- package/dist/registration/index.js +1 -1
- package/dist/registration/index.js.map +1 -1
- package/dist/registration/index.mjs +1 -1
- package/dist/registration/index.mjs.map +1 -1
- package/dist/transport/index.js +1 -1
- package/dist/transport/index.js.map +1 -1
- package/dist/transport/index.mjs +1 -1
- package/dist/transport/index.mjs.map +1 -1
- package/dist/{types-Bm1QvGqI.d.ts → types-DA8LzAOB.d.ts} +6 -0
- package/dist/{types-Blz520bR.d.mts → types-DRb_plOh.d.mts} +14 -0
- package/dist/{types-Blz520bR.d.ts → types-DRb_plOh.d.ts} +14 -0
- package/dist/{types-DBe4hgfT.d.mts → types-vNBvYD7Z.d.mts} +6 -0
- package/dist/ui/index.d.mts +1 -1
- package/dist/ui/index.d.ts +1 -1
- package/dist/verify.d.mts +1 -1
- package/dist/verify.d.ts +1 -1
- package/dist/verify.js +5 -1
- package/dist/verify.js.map +1 -1
- package/dist/verify.mjs +5 -1
- package/dist/verify.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -112,7 +112,10 @@ if (result.identityVerified && result.policyAllowed) {
|
|
|
112
112
|
|
|
113
113
|
#### `apiEndpoint` — runtime-challenge URL
|
|
114
114
|
|
|
115
|
-
`apiEndpoint` is the URL where your agent's
|
|
115
|
+
`apiEndpoint` is the base URL where your agent's own `ChallengeHandler` is mounted (AstraSync POSTs to `{apiEndpoint}/astrasync/challenge`). Endpoints can require the runtime challenge endpoint-wide or per route; an agent that can't answer it is denied there.
|
|
116
|
+
|
|
117
|
+
- **Your own handler:** mount `ChallengeHandler.expressMiddleware()` at `/astrasync/challenge` and set `apiEndpoint`. Since 5.16.0 the handler answers only for counterparties registered for the call in flight — pass `challengeHandler` to `AgentClient` and each `fetch()` registers its destination, or wrap your own calls in `registerPending()` / `removePending()`.
|
|
118
|
+
- **Through a Workforce Guard checkpoint:** leave `apiEndpoint` blank. The checkpoint answers the challenge for every call made through it (it marks those calls `x-astra-via-checkpoint`). A direct call that bypasses the checkpoint is challenged at the checkpoint, which has nothing pending for it, so it fails — unless the agent also has its own handler, which direct calls ring instead.
|
|
116
119
|
|
|
117
120
|
#### Two registration response modes
|
|
118
121
|
|
|
@@ -385,18 +388,25 @@ import {
|
|
|
385
388
|
<GuidanceCard guidance={verificationResult.guidance} />
|
|
386
389
|
```
|
|
387
390
|
|
|
388
|
-
##
|
|
391
|
+
## What the middleware reads from a request
|
|
389
392
|
|
|
390
|
-
|
|
393
|
+
The middleware reads the calling agent's **identity** and passes it to
|
|
394
|
+
verify-access, which your server calls with **your own** API key:
|
|
391
395
|
|
|
392
396
|
1. **Headers** (recommended):
|
|
393
|
-
- `X-Astra-Id`:
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
-
|
|
399
|
-
|
|
397
|
+
- `X-Astra-Id`: the agent's ASTRA-ID. It says which agent is calling; it is
|
|
398
|
+
an identifier, **never a credential**. Anyone can send any agent's id, so
|
|
399
|
+
protect sensitive routes with PDLSS policy, `allowedOwners`, and the
|
|
400
|
+
runtime challenge (which only the real agent's handler, or its Workforce
|
|
401
|
+
Guard checkpoint, can answer).
|
|
402
|
+
- `X-Api-Key` / `Authorization: Bearer …`: forwarded as observed metadata.
|
|
403
|
+
2. **Query parameters** (fallback): `?astraId=ASTRA-xxx`, `?apiKey=xxx`.
|
|
404
|
+
|
|
405
|
+
Calling the AstraSync API itself is different: only an API key or a full
|
|
406
|
+
dashboard session token authenticates. A request that offers only
|
|
407
|
+
`X-Astra-Id` is refused with `401 agent_identity_not_a_credential`, and
|
|
408
|
+
first-step sign-in tokens (before the second factor), refresh tokens and
|
|
409
|
+
passkey elevation tokens are refused with 401.
|
|
400
410
|
|
|
401
411
|
## Verification Response
|
|
402
412
|
|
|
@@ -466,7 +476,22 @@ if (!decision.authorized) {
|
|
|
466
476
|
|
|
467
477
|
## Step-Up Approval
|
|
468
478
|
|
|
469
|
-
|
|
479
|
+
The agent's owner can ask to approve requests ("Ask me") on **any** PDLSS
|
|
480
|
+
dimension, not just spend. verify-access then returns
|
|
481
|
+
`recommendation: 'step_up_required'` with `stepUpApproval` and a `failures[]`
|
|
482
|
+
entry naming the dimension. This is **not** a deny: hold the request and poll.
|
|
483
|
+
|
|
484
|
+
| Dimension | When | `failures[].dimension` |
|
|
485
|
+
| ------------------ | --------------------------------------------------------- | ------------------------------------------- |
|
|
486
|
+
| Limits | value above the Autonomous Limit and below the Hard Limit | `commerce.intent.approval_required` |
|
|
487
|
+
| Purpose | the action is on the owner's approval list | `pdlss.purpose.approval_required` |
|
|
488
|
+
| Duration | outside the agent's time window, set to ask | `pdlss.duration.approval_required` |
|
|
489
|
+
| Scope | a resource, counterparty or jurisdiction set to ask | `pdlss.scope.approval_required` |
|
|
490
|
+
| Self-instantiation | spawning a sub-agent, set to ask | `pdlss.selfInstantiation.approval_required` |
|
|
491
|
+
|
|
492
|
+
The entry names the dimension, never the rule. Once the owner approves, the
|
|
493
|
+
same request (same `attemptId`) passes once. Gate on `recommendation` (or
|
|
494
|
+
`stepUpApproval`), never on a non-empty `failures[]` alone.
|
|
470
495
|
|
|
471
496
|
```typescript
|
|
472
497
|
interface StepUpApprovalInfo {
|
|
@@ -514,6 +539,26 @@ outlive that budget — behind the bridge, either:
|
|
|
514
539
|
Either way the attempt chain above guarantees the post-approval re-drive
|
|
515
540
|
passes without a second approval.
|
|
516
541
|
|
|
542
|
+
### Routes that hold their own approval (v5.17.1)
|
|
543
|
+
|
|
544
|
+
If a route asks the owner for approval itself — it creates its own hold and
|
|
545
|
+
answers `202` while it waits — set `approvalHeldByRoute` so the owner isn't
|
|
546
|
+
asked twice:
|
|
547
|
+
|
|
548
|
+
```ts
|
|
549
|
+
createMiddleware({
|
|
550
|
+
// …
|
|
551
|
+
approvalHeldByRoute: (req) => req.method === 'POST' && req.path === '/signin',
|
|
552
|
+
});
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
For matching requests, a result that only needs the owner's approval reaches
|
|
556
|
+
your handler (`req.agentVerification.requiresStepUp` is true and `failures`
|
|
557
|
+
names each `pdlss.<dimension>.approval_required` or
|
|
558
|
+
`commerce.intent.approval_required`); verify-access creates no approval. Your
|
|
559
|
+
route must then hold the request until the owner approves. Any deny is still
|
|
560
|
+
refused.
|
|
561
|
+
|
|
517
562
|
## Checkout & settlement (astra-pay)
|
|
518
563
|
|
|
519
564
|
The first-party commerce rail: an agent shops under its ASTRA-id, holds NO
|
|
@@ -823,18 +868,38 @@ The installer registers the `astrasync-claude-hook` binary in Claude Code's `set
|
|
|
823
868
|
- **MCP governance** — set `"governMcp": true` to govern MCP tools too; `mcp__{server}__{tool}` maps to purpose `{server}.{tool}` in the policy.
|
|
824
869
|
- **Uninstall** — `astrasync-guard uninstall-claude-code` removes the hook; policy and traces are left in place.
|
|
825
870
|
|
|
871
|
+
## Fleet & Workforce Guard CLI
|
|
872
|
+
|
|
873
|
+
Organisation-managed commands (Enterprise Governance → Fleet). Run them as
|
|
874
|
+
`astrasync guard <command>`.
|
|
875
|
+
|
|
876
|
+
| Command | What it does |
|
|
877
|
+
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
878
|
+
| `enroll <token>` | Enrol this machine in your organisation's fleet. User scope by default; `--system` enrols the whole machine (needs administrator rights; machine scope wins over user scope). `--api-base-url <url>` targets another environment. |
|
|
879
|
+
| `fleet-status` | Show the enrolment, the signed policy and the last heartbeat. |
|
|
880
|
+
| `managed-settings` | Print MDM templates for Claude Code and/or Codex (`--claude`, `--codex`, `--out <dir>` to write files). Machines must also be enrolled with `enroll <token> --system`. |
|
|
881
|
+
| `git-route` | Route this machine's `git fetch` / `git push` to github.com through your Workforce Guard checkpoint (`--checkpoint <url> --slug <connector> --agent <ASTRA-id>`, `--upstream` default `github`; `--undo` removes it). `git commit` and local hooks are unchanged. |
|
|
882
|
+
| `git-credential` | The git credential helper `git-route` installs. Answers with the agent's ASTRA-id and this machine's device credential; no git host token is stored on the machine. Not run by hand. |
|
|
883
|
+
| `browse` | Sign an agent in to its owner's dashboard from this machine and run its device proxy (`--checkpoint <url> --slug <agent>`, `--upstream` default `astrasync-api`, `--port`, `--host`). Needs an enrolled machine and `dashboard.signin` in the agent's policy. See CHANGELOG 5.17.0. |
|
|
884
|
+
|
|
885
|
+
Calls an agent makes through its checkpoint have their runtime challenge
|
|
886
|
+
answered by the checkpoint; the agent needs no `apiEndpoint` of its own.
|
|
887
|
+
|
|
826
888
|
## Changelog
|
|
827
889
|
|
|
828
|
-
|
|
890
|
+
Newest first. Full detail for every release is in [CHANGELOG.md](./CHANGELOG.md).
|
|
829
891
|
|
|
830
|
-
|
|
831
|
-
- The MCP middleware now captures header-level `ObservedMetadata` on every verify-access call (parity with the express/nextjs adapters).
|
|
832
|
-
- The edge product is the **Trusted Agent Gateway (TAG)** — see the new README sections above.
|
|
892
|
+
### v5.17.1 — one approval for a route that holds its own
|
|
833
893
|
|
|
834
|
-
|
|
894
|
+
- `approvalHeldByRoute` (Express): a route that holds its own owner approval receives an approval-only result instead of a refusal, and verify-access creates no approval — the owner approves once.
|
|
895
|
+
- A hold on purpose, duration, scope or sub-agents is no longer described as a spending limit; step-up results carry the server's `failures`.
|
|
896
|
+
- `astrasync guard browse` says why a sign-in was refused, and stops at once on a refused status check.
|
|
835
897
|
|
|
836
|
-
|
|
837
|
-
|
|
898
|
+
### v5.17.0 — `astrasync guard browse`
|
|
899
|
+
|
|
900
|
+
- `astrasync guard browse` signs an agent in to its owner's dashboard through its
|
|
901
|
+
Workforce Guard checkpoint, bound to this machine's device credential, and runs
|
|
902
|
+
the local device proxy its automation browser uses.
|
|
838
903
|
|
|
839
904
|
### v5.16.0 — ChallengeHandler auto-include removed
|
|
840
905
|
|
|
@@ -844,6 +909,17 @@ The installer registers the `astrasync-claude-hook` binary in Claude Code's `set
|
|
|
844
909
|
registers each call while it is in flight; otherwise call
|
|
845
910
|
`registerPending()` / `removePending()` around each interaction.
|
|
846
911
|
|
|
912
|
+
### v4.1.0 — Trusted Agent Gateway naming + MCP metadata capture
|
|
913
|
+
|
|
914
|
+
- The Next.js unverified-agent overlay is now the **verification interstitial**: `showInterstitial` option, `VerificationInterstitial` / `useVerificationInterstitial` / `VerificationInterstitialProps`. The old `showCommerceShield` / `CommerceShield` / `useCommerceShield` / `CommerceShieldProps` names keep working as deprecated aliases until the next major (`showInterstitial` wins when both options are set).
|
|
915
|
+
- The MCP middleware now captures header-level `ObservedMetadata` on every verify-access call (parity with the express/nextjs adapters).
|
|
916
|
+
- The edge product is the **Trusted Agent Gateway (TAG)** — see the new README sections above.
|
|
917
|
+
|
|
918
|
+
### v3.8.0 — Settlement voucher wire format v2
|
|
919
|
+
|
|
920
|
+
- **BREAKING type change:** `SettlementArtifact.binding` is now a union — `StablecoinSettlementBinding` (v2: `amountMinor` integer minor units + `assetDecimals`, pinned `chainId`/`tokenContract`/CAIP-19 `asset`) or `FiatSettlementBinding` (processor artifacts keep major-unit `amount`). The float `amount` field is gone from stablecoin bindings. Matches the server's `ver: 2` wire format, which redeems/verifies reject other versions of.
|
|
921
|
+
- Settlement Artifacts docs rewritten: voucher lifecycle endpoints (verify / redeem / JWKS), expected-value gates on redeem, revocation semantics, and an explicit **guarantee boundary** — what a successful redeem proves and what it doesn't (redeem proves the authorization is live and single-use; it does not check or move funds).
|
|
922
|
+
|
|
847
923
|
### v3.7.1 — ChallengeHandler auto-include (removed in 5.16.0)
|
|
848
924
|
|
|
849
925
|
- `ChallengeHandler` auto-includes the incoming `counterpartyId`/`counterpartyUrl` in the challenge response when no counterparties were pre-registered via `registerPending()` — the correct default for agents that never call it. Pre-registered whitelists behave exactly as before.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RequestHandler, Request } from 'express';
|
|
2
|
-
import { d as VerificationResult, E as ExpressMiddlewareOptions, a as AstraSyncCredentials } from '../types-
|
|
2
|
+
import { d as VerificationResult, E as ExpressMiddlewareOptions, a as AstraSyncCredentials } from '../types-DRb_plOh.mjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* AstraSync Universal Verification Gateway - Express Middleware
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RequestHandler, Request } from 'express';
|
|
2
|
-
import { d as VerificationResult, E as ExpressMiddlewareOptions, a as AstraSyncCredentials } from '../types-
|
|
2
|
+
import { d as VerificationResult, E as ExpressMiddlewareOptions, a as AstraSyncCredentials } from '../types-DRb_plOh.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* AstraSync Universal Verification Gateway - Express Middleware
|
package/dist/adapters/express.js
CHANGED
|
@@ -469,7 +469,7 @@ function routePolicyOf(route) {
|
|
|
469
469
|
}
|
|
470
470
|
|
|
471
471
|
// src/version.ts
|
|
472
|
-
var SDK_VERSION = "5.17.
|
|
472
|
+
var SDK_VERSION = "5.17.1";
|
|
473
473
|
|
|
474
474
|
// src/http.ts
|
|
475
475
|
var SDK_USER_AGENT = `astrasync-sdk/${SDK_VERSION}`;
|
|
@@ -771,6 +771,7 @@ function getCacheKey(request, counterpartyId) {
|
|
|
771
771
|
request.enableRuntimeChallenge ? "1" : "0",
|
|
772
772
|
// 5.16.0: route policy changes the verdict (owners / challenge).
|
|
773
773
|
request.routePolicy ? JSON.stringify(request.routePolicy) : "",
|
|
774
|
+
request.approvalHeldByRoute ? "1" : "0",
|
|
774
775
|
// callerMetadata fields contribute to risk model; include the ones
|
|
775
776
|
// backend reads. sourceIp/userAgent/forwardedFor change per-request
|
|
776
777
|
// so their inclusion effectively forces a re-check for any varying
|
|
@@ -978,6 +979,7 @@ async function callVerifyAccessAPI(config, request) {
|
|
|
978
979
|
if (requestData.enableRuntimeChallenge)
|
|
979
980
|
body.enableRuntimeChallenge = requestData.enableRuntimeChallenge;
|
|
980
981
|
if (requestData.routePolicy) body.routePolicy = requestData.routePolicy;
|
|
982
|
+
if (requestData.approvalHeldByRoute) body.approvalHeldByRoute = true;
|
|
981
983
|
if (requestData.createSession) body.createSession = requestData.createSession;
|
|
982
984
|
if (requestData.durationRequired) body.durationRequired = requestData.durationRequired;
|
|
983
985
|
if (requestData.counterpartyType) body.counterpartyType = requestData.counterpartyType;
|
|
@@ -1309,6 +1311,8 @@ async function verify(config, request, options) {
|
|
|
1309
1311
|
} else if (result.recommendation === "step_up_required" && result.identityVerified) {
|
|
1310
1312
|
result.requiresStepUp = true;
|
|
1311
1313
|
result.denialReasons = serverDenialReasons ?? result.recommendationReasons ?? ["Step-up verification required"];
|
|
1314
|
+
const stepUpFailures = apiResponse.access?.failures;
|
|
1315
|
+
if (stepUpFailures?.length) result.failures = stepUpFailures;
|
|
1312
1316
|
}
|
|
1313
1317
|
const escalates = result.requiresStepUp === true || result.requiresApproval === true || result.recommendation === "step_up_required";
|
|
1314
1318
|
if (mergedConfig.cacheTtl !== 0 && result.recommendation !== "deny" && !escalates) {
|
|
@@ -1543,12 +1547,31 @@ function requiresHumanApproval(result) {
|
|
|
1543
1547
|
return result.requiresStepUp === true || result.requiresApproval === true;
|
|
1544
1548
|
}
|
|
1545
1549
|
function annotateApprovalRequired(result) {
|
|
1546
|
-
result
|
|
1547
|
-
...result.
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
+
if (isDimensionApproval(result)) {
|
|
1551
|
+
result.denialReasons = [DIMENSION_APPROVAL_REASON, ...result.denialReasons ?? []];
|
|
1552
|
+
return;
|
|
1553
|
+
}
|
|
1554
|
+
const named = (result.failures ?? []).some(
|
|
1555
|
+
(f) => f.dimension === "commerce.intent.approval_required"
|
|
1556
|
+
);
|
|
1557
|
+
if (!named) {
|
|
1558
|
+
result.failures = [
|
|
1559
|
+
...result.failures ?? [],
|
|
1560
|
+
{ dimension: "commerce.intent.approval_required", message: APPROVAL_REASON }
|
|
1561
|
+
];
|
|
1562
|
+
}
|
|
1550
1563
|
result.denialReasons = [APPROVAL_REASON, ...result.denialReasons ?? []];
|
|
1551
1564
|
}
|
|
1565
|
+
var DIMENSION_APPROVAL_REASON = "The agent's owner asked to approve this kind of request. The owner can approve or deny it in the AstraSync dashboard.";
|
|
1566
|
+
var DIMENSION_APPROVAL = /^pdlss\.[a-z_]+\.approval_required$/;
|
|
1567
|
+
function isDimensionApproval(result) {
|
|
1568
|
+
const f = result.failures ?? [];
|
|
1569
|
+
return f.some((x) => DIMENSION_APPROVAL.test(x.dimension)) && !f.some((x) => x.dimension === "commerce.intent.approval_required");
|
|
1570
|
+
}
|
|
1571
|
+
function onlyNeedsApproval(result) {
|
|
1572
|
+
if (!result.policyAllowed || !requiresHumanApproval(result)) return false;
|
|
1573
|
+
return (result.failures ?? []).every((f) => /\.approval_required$/.test(f.dimension));
|
|
1574
|
+
}
|
|
1552
1575
|
|
|
1553
1576
|
// src/pdlss-pre-check.ts
|
|
1554
1577
|
function performCounterpartyPreCheck(routeConfig, astraCreds, purpose) {
|
|
@@ -2512,6 +2535,7 @@ function createMiddleware(options) {
|
|
|
2512
2535
|
const attemptHeader = req.headers["x-astra-attempt-id"];
|
|
2513
2536
|
const inboundAttemptId = typeof attemptHeader === "string" && /^att_[0-9a-f]{32}$/.test(attemptHeader) ? attemptHeader : void 0;
|
|
2514
2537
|
const commerceArtifacts = detectExpressCommerce(req);
|
|
2538
|
+
const heldByRoute = options.approvalHeldByRoute?.(req) === true;
|
|
2515
2539
|
const verificationRequest = {
|
|
2516
2540
|
credentials,
|
|
2517
2541
|
...commerceArtifacts && { commerceArtifacts },
|
|
@@ -2523,6 +2547,7 @@ function createMiddleware(options) {
|
|
|
2523
2547
|
counterpartyType: config.counterpartyType || "api",
|
|
2524
2548
|
enableRuntimeChallenge,
|
|
2525
2549
|
...routePolicyOf(routeConfig) && { routePolicy: routePolicyOf(routeConfig) },
|
|
2550
|
+
...heldByRoute && { approvalHeldByRoute: true },
|
|
2526
2551
|
durationRequired: astraCreds?.pdlss?.duration?.maxSessionDuration,
|
|
2527
2552
|
...txValue !== void 0 && { transactionValue: txValue },
|
|
2528
2553
|
...txCurrency && { currency: txCurrency },
|
|
@@ -2579,6 +2604,9 @@ function createMiddleware(options) {
|
|
|
2579
2604
|
onDenied(result, req, res);
|
|
2580
2605
|
return;
|
|
2581
2606
|
}
|
|
2607
|
+
if (heldByRoute && !result.stepUpApproval && onlyNeedsApproval(result)) {
|
|
2608
|
+
return next();
|
|
2609
|
+
}
|
|
2582
2610
|
if (requiresHumanApproval(result)) {
|
|
2583
2611
|
annotateApprovalRequired(result);
|
|
2584
2612
|
if (shouldRecordDecisions && sessionId) {
|