Skip to content

Hosted instance mints download tokens with 'resuleId', verifier reads 'resumeId', so every PDF link 401s #3525

Description

@mazshakibaii

What happens

Every PDF download link minted on rxresu.me right now is rejected by the endpoint that serves it. Minting and verification disagree on the payload field name, and the verifying side is the code in this repo.

Reproduced today at 17:33Z on 5.3.1:

  1. Ask for a link (I used the download_resume_pdf MCP tool; the response says expiresInSeconds: 600).
  2. Load it immediately.
$ curl -i "$downloadUrl"
HTTP/2 401
Unauthorized

Six attempts on one link, then 24 in parallel on another, all 401. Same key, same account, same machine, nothing changed in between.

The token is fresh, and it is not an expiry

Decoded payload of a link minted five seconds before the request:

{
  "v": 1,
  "resuleId": "01a0a997-...",
  "userId": "019bef8d-...",
  "target": "resume",
  "expiresAt": 1789839822731,
  "issuedAt": 1789839222731
}

minted 17:33:42Z, requested 17:33:47Z, expires 17:43:42Z. Ids truncated.

resuleId is not the field the verifier reads. In packages/api/src/features/resume/pdf-download-url.ts at v5.3.1:

if (typeof payload.resumeId !== "string" || payload.resumeId.length === 0) return null;

That returns malformed, and apps/server/src/http/resume-pdf.ts maps everything other than expired to 401, so from outside it is indistinguishable from a bad signature. I checked out of paranoia: a code search over this repo finds verifyResumePdfDownloadToken in five places and resuleId in none, so whatever minted that payload is not the code in the tree.

It worked earlier today, which points at a skewed deployment

At 13:13Z a link minted the same way downloaded a PDF normally. That token carries the identical resuleId payload. So the verifier changed under me during the day: what served /api/resumes/:id/pdf this morning accepted this payload and what serves it now does not. That reads like a rollout where the minting side is still on older code and the verifying side moved to 5.3.1, and links started failing as the old instances drained. It would also be worth knowing whether the minting path is meant to be shared, since the same resuleId string appears on both the MCP and the web-facing links I have seen.

Meanwhile, in the same seconds

GET /api/openapi/resumes            -> 200
GET /api/openapi/resumes/<id>/pdf   -> 200, 44546 bytes of PDF
GET /api/health                     -> 200, database healthy, uptime 2.3 days

So rendering, storage and the account are fine. Only the token route rejects its own output. The API key route is what I use now, though it means every client that follows the downloadUrl the API hands back is broken, including the MCP tool that exists to produce it.

Possibly related

Earlier in the afternoon, for roughly half an hour, the same API key got 401 from /api/openapi/resumes, /mcp refused the initialize handshake with 401, and one MCP write call failed and then succeeded on retry. All of it cleared without a key rotation. If a rollout was in flight then too, that would fit, but I have not tested it.

Happy to keep probing the hosted instance while it reproduces.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions