Skip to content

fix: add client-side challenge expiration validation#233

Closed
JasonOA888 wants to merge 1 commit into
wevm:mainfrom
JasonOA888:fix/client-side-challenge-expiration
Closed

fix: add client-side challenge expiration validation#233
JasonOA888 wants to merge 1 commit into
wevm:mainfrom
JasonOA888:fix/client-side-challenge-expiration

Conversation

@JasonOA888

Copy link
Copy Markdown

Summary

Fixes #198

Problem

When a client receives a Challenge with an expires timestamp that has already passed, the client still attempts to create a credential and make the payment. This results in:

  1. Wasted user time - wallet interactions that will be rejected
  2. Unnecessary network round-trips
  3. User confusion (why did my payment fail?)

The server already validates expiration (src/server/Mppx.ts:359-367), but by then the client has already connected wallet and signed transactions.

Solution

Adds client-side expiration validation at two code paths:

  1. Fetch.from() - auto-402 interceptor, after challenge matching
  2. Mppx.createCredential() - manual API, after challenge parsing

Both throw PaymentExpiredError (already defined in Errors.ts) when challenge.expires is set and in the past.

Changes

// src/client/internal/Fetch.ts - after finding matching challenge
validateChallengeExpiration(challenge)

// src/client/Mppx.ts - in createCredential()
if (challenge.expires && new Date(challenge.expires) < new Date()) {
  throw new Errors.PaymentExpiredError({ expires: challenge.expires })
}

Testing

// Expired challenge
const challenge = { expires: "2025-01-01T00:00:00Z", ... }
await mppx.createCredential(response)
// Throws: PaymentExpiredError: Payment expired at 2025-01-01T00:00:00Z.

Impact

  • Type: UX improvement
  • Risk: Low - fails faster with clearer error
  • Breaking: No - just changes when the error is thrown

Adds early rejection of expired challenges on the client side, before
attempting to create credentials. This improves UX by failing fast
with a clear error instead of wasting user time on wallet interactions
that will be rejected by the server.

Two code paths now validate expiration:
1. Fetch.from() - auto-402 interceptor, after challenge matching
2. Mppx.createCredential() - manual API, after challenge parsing

Both throw PaymentExpiredError (already defined in Errors.ts) when
challenge.expires is set and in the past.

This mirrors the existing server-side validation at src/server/Mppx.ts:359-367.

Fixes wevm#198
@brendanjryan

Copy link
Copy Markdown
Collaborator

Thanks for suggesting!

We initially didn't implement this, since the server is authoritative and the client could have clock skew from the server expiry. We will consider this in a new version of the SDK if we see it more frequently

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.

UX: Client-side should validate challenge expiration before creating credential

2 participants