Skip to content

feat(sdk): add WebBotAuthResource for Web Bot Auth header minting#105

Merged
nvp-stripe merged 4 commits into
mainfrom
nvp/link-cli-web-bot-auth
May 20, 2026
Merged

feat(sdk): add WebBotAuthResource for Web Bot Auth header minting#105
nvp-stripe merged 4 commits into
mainfrom
nvp/link-cli-web-bot-auth

Conversation

@nvp-stripe
Copy link
Copy Markdown
Contributor

@nvp-stripe nvp-stripe commented May 19, 2026

r? @kreese-stripe @danhill-stripe @bendavis-stripe

Summary and motivation

Adds WebBotAuthResource to @stripe/link-sdk the SDK component that fetches pre-signed Web Bot Auth headers from the Link backend so AI agents can prove their identity to merchant sites protected by Cloudflare bot filtering.

What this adds:

  • New WebBotAuthBlock type in mirroring the backend response shape:
    { signature, signature_input, signature_agent, authority, expires_at }.
  • New IWebBotAuthResource interface with a single method: getHeaders(url: string): Promise<WebBotAuthBlock>.
  • WebBotAuthResource class implementing the interface. On getHeaders(url):
    1. Extracts the authority (hostname) from the URL.
    2. Checks an in-memory per-authority cache; returns the cached block if it expires
      more than 30 seconds in the future.
    3. Otherwise POSTs to POST /v1/agent_identity/credentials (authenticated with the
      user's Link OAuth bearer token) with { url } as the JSON body.
    4. Extracts the web_bot_auth block from the response, caches it keyed by hostname,
      and returns it.
    5. On 401, refreshes the access token once and retries (same pattern as all other SDK resources).
  • WebBotAuthResource wired into ResourceFactory in the CLI package with lazy caching,
    same as SpendRequestResource, PaymentMethodsResource, etc.

Why:

AI agents using Link CLI to complete checkout on merchant sites get blocked by Cloudflare and Vercel's automated bot detection: their traffic is indistinguishable from scraper traffic.
Web Bot Auth is an IETF-draft protocol where registered bots attach a cryptographic signature to every request, allowing CDN/WAF managed rulesets to bypass bot challenges without user interaction.

Signatures are valid for 10 minutes. The per-authority cache avoids a round-trip to api.link.com on every request to the same merchant domain within that window.

Test plan

  • packages/sdk/src/resources/__tests__/web-bot-auth.test.ts (11 new unit tests).

  • packages/cli/src/utils/__tests__/resource-factory.test.ts updated:

    • createWebBotAuthResource() returns cached instance on repeated calls
    • Returns instanceof WebBotAuthResource

@nvp-stripe nvp-stripe requested a review from a team as a code owner May 19, 2026 22:06
@nvp-stripe nvp-stripe changed the title WIP feat(sdk): add WebBotAuthResource for Web Bot Auth header minting feat(sdk): add WebBotAuthResource for Web Bot Auth header minting May 19, 2026
Copy link
Copy Markdown
Contributor

@danhill-stripe danhill-stripe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good! couple small comments.

this.getAccessToken = config.getAccessToken;
this.fetchImpl = requireFetchImplementation(config);
this.credentialsEndpoint = `${config.apiBaseUrl}/v1/agent_identity/credentials`;
this.logger = config.logger;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also defaultHeaders?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

defaultHeaders is already applied by resolveLinkSdkConfig via reateDefaultHeadersFetch. added a comment.


this.cache.set(authority, {
block: webBotAuth,
expiresAt: Date.parse(webBotAuth.expires_at),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handle if expires_at is bad/malformed?

Copy link
Copy Markdown
Contributor Author

@nvp-stripe nvp-stripe May 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added explicit NaN check after Date.parse. Previously a bad expires_at would produce NaN as expiresAt, causing the cache condition Date.now() < NaN to silently always be false; every subsequent call would re-fetch. Now throws a LinkSdkError with the bad value in the message. Added a test for this case.

@nvp-stripe nvp-stripe requested a review from danhill-stripe May 20, 2026 02:00
@nvp-stripe nvp-stripe merged commit cc4adb2 into main May 20, 2026
5 checks passed
@nvp-stripe nvp-stripe deleted the nvp/link-cli-web-bot-auth branch May 20, 2026 14:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants