modelcontextprotocol / modelcontextprotocol/typescript-sdk
authExtensions docs say custom claims override reserved JWT claims, but implementation keeps reserved claims authoritative
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 13.4k
- Forks
- 2.2k
- Avg merge
- 3d 15h
- Merged PRs (30d)
- 4
Description
Summary
PrivateKeyJwtProviderOptions.claims currently documents overlapping custom claims as taking precedence over the SDK's standard JWT claims, but the implementation keeps the reserved claims authoritative.
This looks like a docs / contract mismatch rather than a runtime bug or security issue.
Current docs
packages/client/src/client/authExtensions.ts says:
These are merged with the standard claims (
iss,sub,aud,exp,iat,jti), with custom claims taking precedence for any overlapping keys.
Actual behavior
createPrivateKeyJwtAuth() constructs claims from { ...baseClaims, ...options.claims }, but then immediately calls:
.setIssuer(options.issuer).setSubject(options.subject).setAudience(audience).setIssuedAt(now).setExpirationTime(now + lifetimeSeconds).setJti(jti)
Those setters overwrite overlapping values from options.claims, so the SDK's reserved claims remain authoritative.
Minimal reproduction
const addClientAuth = createPrivateKeyJwtAuth({
issuer: 'client-id',
subject: 'client-id',
privateKey: 'a-string-secret-at-least-256-bits-long',
alg: 'HS256',
audience: 'https://aud.example.com',
claims: {
iss: 'override-issuer',
sub: 'override-subject',
aud: 'https://override.example.com',
tenant_id: 'org-123'
}
});
Decoding the resulting JWT shows:
iss === 'client-id'sub === 'client-id'aud === 'https://aud.example.com'tenant_id === 'org-123'
So additional custom claims are included, but overlapping reserved claims are not overridden.
Why this matters
This can mislead users into thinking they can customize reserved JWT claims through claims, when in practice only non-overlapping claims are honored.
Suggested resolution
I think the smallest fix is to align the docs/tests with the current runtime behavior:
- clarify that additional custom claims are included
- clarify that reserved standard claims are still set explicitly by the SDK and are not overridden
- add a regression test showing the current behavior
If maintainers prefer the opposite behavior, that would likely deserve a separate design discussion because it changes runtime semantics.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in packages/client/src/client/authExtensions.ts and read the PrivateKeyJwtProviderOptions.claims documentation alongside createPrivateKeyJwtAuth(). Locate the related auth extension tests, then update the wording to explain that additional claims are included while reserved standard claims remain SDK-controlled. Done means the documentation matches runtime behavior and a regression test demonstrates the reserved-claim behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- authentication, documentation
- Issue type
- Documentation
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 74/100