Skip to content

fix(client/auth): sign private_key_jwt with an Ed25519 PEM key - #3165

Open
rodrigogaraujo wants to merge 1 commit into
modelcontextprotocol:mainfrom
rodrigogaraujo:fix/private-key-jwt-eddsa
Open

rodrigogaraujo wants to merge 1 commit into
modelcontextprotocol:mainfrom
rodrigogaraujo:fix/private-key-jwt-eddsa

Conversation

@rodrigogaraujo

Copy link
Copy Markdown

createPrivateKeyJwtAuth(), which PrivateKeyJwtProvider uses, now imports a PEM string key for EdDSA and Ed25519 instead of throwing Unsupported algorithm EdDSA.

Motivation and Context

docs/clients/machine-auth.md says privateKey "accepts a PEM string, a Uint8Array, or a JWK object". With an Ed25519 PKCS#8 PEM string and algorithm: 'EdDSA', addClientAuthentication throws before the token request, so auth() fails. The same key works as a Uint8Array holding the same PEM text, or as a JWK:

import { exportPKCS8, generateKeyPair } from 'jose';
import { PrivateKeyJwtProvider } from '@modelcontextprotocol/client';

const { privateKey } = await generateKeyPair('EdDSA', { extractable: true });
const pem = await exportPKCS8(privateKey);

for (const key of [pem, new TextEncoder().encode(pem)]) {
    const provider = new PrivateKeyJwtProvider({
        clientId: 'my-client',
        privateKey: key,
        algorithm: 'EdDSA',
        expectedIssuer: 'https://auth.example.com'
    });
    const params = new URLSearchParams();
    try {
        await provider.addClientAuthentication(new Headers(), params, 'https://auth.example.com/token', undefined);
        console.log(typeof key === 'string' ? 'PEM string' : 'Uint8Array', '-> signed');
    } catch (error) {
        console.log(typeof key === 'string' ? 'PEM string' : 'Uint8Array', '->', String(error));
    }
}

On main (1e608ca):

PEM string -> Error: Unsupported algorithm EdDSA
Uint8Array -> signed

The string-key branch in packages/client/src/client/authExtensions.ts sent only RS*, ES* and PS* to jose.importPKCS8; HS* is a shared secret and anything else throws. This change adds alg.startsWith('Ed') to that branch. jose 6 imports Ed25519 PKCS#8 keys for both EdDSA and Ed25519 and rejects anything it does not support. none still throws Unsupported algorithm none, as the existing tests expect. The SDK's DPoP support already lists EdDSA in DPOP_SUPPORTED_ALGS.

How Has This Been Tested?

  • New it.each(['EdDSA', 'Ed25519']) test in packages/client/test/client/authExtensions.test.ts. It generates an Ed25519 key, passes it as a PEM string, and verifies the signed assertion with the public key (jose.jwtVerify). Both cases fail on unmodified main with Error: Unsupported algorithm EdDSA / Ed25519 and pass with this change.
  • pnpm --filter @modelcontextprotocol/client test passes (44 files, 1080 tests), as do typecheck and lint.

Breaking Changes

None. Only algorithms that used to throw before signing are affected.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

Includes a patch changeset for @modelcontextprotocol/client. The allow-list stays, so none and unknown values keep the SDK's own error; jose 6 would also import ML-DSA keys, which this change leaves out to stay with the reported case. v1.x has the same check in src/client/auth-extensions.ts.

AI assistance

Prepared with Claude Code (Claude Opus 5.5): investigation, fix, tests and this description. The reproduction was run on main; the tests listed above were run on main and with this change.

🤖 Generated with Claude Code

createPrivateKeyJwtAuth() routed a PEM string to importPKCS8 only for
RS*, ES* and PS* algorithms, so PrivateKeyJwtProvider with
algorithm 'EdDSA' or 'Ed25519' threw "Unsupported algorithm EdDSA"
before signing. The same key passed as a Uint8Array or a JWK already
worked. Route Ed* algorithms to importPKCS8 as well; jose still rejects
any it does not support.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@rodrigogaraujo
rodrigogaraujo requested a review from a team as a code owner November 9, 2026 01:34
@changeset-bot

changeset-bot Bot commented Nov 9, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: d6034ee

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 6 packages
Name Type
@modelcontextprotocol/client Patch
@modelcontextprotocol/codemod Patch
@modelcontextprotocol/core Patch
@modelcontextprotocol/server-legacy Patch
@modelcontextprotocol/server Patch
@modelcontextprotocol/core-internal Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Nov 9, 2026

Copy link
Copy Markdown

Open in StackBlitz

@modelcontextprotocol/client

npm i https://pkg.pr.new/@modelcontextprotocol/client@3165

@modelcontextprotocol/codemod

npm i https://pkg.pr.new/@modelcontextprotocol/codemod@3165

@modelcontextprotocol/core

npm i https://pkg.pr.new/@modelcontextprotocol/core@3165

@modelcontextprotocol/server

npm i https://pkg.pr.new/@modelcontextprotocol/server@3165

@modelcontextprotocol/server-legacy

npm i https://pkg.pr.new/@modelcontextprotocol/server-legacy@3165

@modelcontextprotocol/express

npm i https://pkg.pr.new/@modelcontextprotocol/express@3165

@modelcontextprotocol/fastify

npm i https://pkg.pr.new/@modelcontextprotocol/fastify@3165

@modelcontextprotocol/hono

npm i https://pkg.pr.new/@modelcontextprotocol/hono@3165

@modelcontextprotocol/node

npm i https://pkg.pr.new/@modelcontextprotocol/node@3165

commit: d6034ee

@claude claude Bot added the v2 Ideas, requests and plans for v2 of the SDK which will incorporate major changes and fixes label Nov 9, 2026

This branch has not been deployed

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

Labels

v2 Ideas, requests and plans for v2 of the SDK which will incorporate major changes and fixes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant