Skip to content

Instantly share code, notes, and snippets.

@ashhadsheikh
Created February 6, 2026 08:17
Show Gist options
  • Select an option

  • Save ashhadsheikh/93a2c31b34418cd3d6b624382031231c to your computer and use it in GitHub Desktop.

Select an option

Save ashhadsheikh/93a2c31b34418cd3d6b624382031231c to your computer and use it in GitHub Desktop.
Investigation: 401 on POST /api/v1/merchant_codes/:code/onboard (Production)

Investigation: 401 on POST /api/v1/merchant_codes/:code/onboard

Date: 2026-02-06 Environment: Production (backend-v1.1.78)

Summary

4 recent 401 errors on the merchant code onboard endpoint, all from the same device/IP, caused by invalid Ed25519 request signature.

Status Code Distribution (Last 7 Days)

Status Count Meaning
201 20 Success
401 4 Unauthorized (signature failure)
409 4 Conflict (user already a merchant)
422 3 Validation error (code state issues)

Total: 31 requests, ~13% failure rate from auth

Root Cause

All 4 x 401s come from the same user/device:

  • IP: 39.49.164.59
  • Public Key: OybhW257V5u6MD3C4tKjXLRShrA33hWVFaDaSsAzwII=
  • Merchant Code: CvbVT2
  • Time Window: 2026-02-05 17:18 – 17:43 UTC (4 attempts in 25 min)
  • City/Country: Lahore, Pakistan

Error from logs:

Accounts::VerifyRequestSignature::Service uncaught exception:
  Accounts::VerifyRequestSignature::Support::Errors::InvalidSignatureError
  - Invalid signature: signature was forged/corrupt

The signature verification service is called 3 times per request (retry logic in the auth pipeline) and fails each time. After all attempts fail, the authenticate_request before_action halts the chain and returns 401.

Detailed Request Trace (Request ID: 3ed07b20-87e9-41f7-802e-007a86c0b194)

[17:43:32.796] [info]  Calling VerifyRequestSignature::Service
                        public_key: OybhW257V5u6MD3C4tKjXLRShrA33hWVFaDaSsAzwII=
                        timestamp:  1770313412
                        method:     POST
                        path:       /api/v1/merchant_codes/CvbVT2/onboard
                        body:       {"data[address]":"F7GR+G49 Shams mobile & nadra e sahulat...",
                                     "data[store_photo]":"6569e030..."}

[17:43:32.796] [error] InvalidSignatureError - Invalid signature: signature was forged/corrupt
[17:43:32.801] [info]  Calling VerifyRequestSignature::Service (retry 2) — same args
[17:43:32.804] [error] InvalidSignatureError - signature was forged/corrupt
[17:43:32.806] [info]  Calling VerifyRequestSignature::Service (retry 3) — same args
[17:43:32.806] [error] InvalidSignatureError - signature was forged/corrupt
[17:43:32.805] [info]  Filter chain halted as :authenticate_request rendered or redirected
[17:43:32.807] [info]  Completed #onboard — 401 Unauthorized (16.7ms, 0 queries, user_id: null)

Comparison: Successful 201 vs Failed 401

Detail 401 (Failed) 201 (Succeeded)
Signature verification InvalidSignatureError - forged/corrupt succeeded in 0.0s
User ID resolved null (never authenticated) ae3854b0-c8bc-475d-9cd0-0cec63c001ab
DB queries 0 (halted before controller action) Multiple (S3 upload, service calls)
Public key OybhW257V5u6... 5s7xFTVjxBGXfB8JySQQ327oj7MEFT0rTqmS0S/m02o=
Body format Same data[field] + store_photo hash Same data[field] + store_photo hash
Request duration 16.7ms 475ms

Likely Causes

  1. Multipart body serialization mismatch — The signing payload shows "data[store_photo]":"6569e030..." (a SHA hash of the file), while the actual request sends the file as multipart form data. If the client serializes the body for signing differently than the server reconstructs it for verification, the signature won't match. The & character in the address (Shams mobile & nadra e sahulat) could also be encoded differently between signing and verification.

  2. Keypair mismatch — The device may be signing with a different private key than the one corresponding to the public key OybhW257V5u6... being sent in the header.

  3. Corrupted device keys — The device may need to re-register its keys (re-login / re-onboard device).

Recommendation

  • Check if public key OybhW257V5u6MD3C4tKjXLRShrA33hWVFaDaSsAzwII= is registered in accounts_devices or accounts_embedded_wallets table
  • If not registered, the user needs to re-authenticate to register their device
  • If registered, investigate the client-side signing logic for multipart requests — specifically how store_photo file data is hashed and how special characters (&) in form fields are encoded during the signing step
  • Consider adding more detailed logging to the signature verification service to show the expected vs received signing payload for easier debugging
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment