Skip to content

fix: add client-side challenge expiration validation#233

Open
JasonOA888 wants to merge 1 commit intowevm:mainfrom
JasonOA888:fix/client-side-challenge-expiration
Open

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

Conversation

@JasonOA888
Copy link

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
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

1 participant