`JWTVerifier` answers 500 instead of 401 for a PS256 token when it verifies with an EC key (jwt < 3.3.0)
Les mainteneurs répondent en général sous 1 jour
Évaluation
- Difficulté
- 3/5
- Temps estimé
- 1-2 jours
- Accessibilité débutants
- 35/100
- Type d'issue
- Bug
- Clarté
- Clairement spécifiée
- Activité
- À l'abandon
- Stack technique
- ruby
- Domaine
- authentication, security
Piste de recherche
Start with lib/mcp/server/oauth/jwt_verifier.rb at the algorithm configuration and verify rescue paths, then follow the middleware error handling in lib/mcp/server/oauth/middleware.rb. Reproduce with the provided EC key and PS256 token on jwt 3.2.0, then check the middleware response. Done when this case returns 401 invalid_token instead of 500; the issue requests regression coverage for key:, jwks: and jwks_uri:.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Description
JWTVerifier allows PS256, PS384 and PS512 together with ES256, ES384 and ES512 by default (jwt_verifier.rb:40), whatever the key type. With an EC key, a static EC JWKS or a fetched EC JWKS, a token whose header says alg: PS256 makes jwt 3.2.0 and earlier call verify_pss on the EC key. That raises NoMethodError.
JWTVerifier#verify rescues only jwt errors (jwt_verifier.rb:92-110). So OAuth::Middleware (middleware.rb:45-51) and the Streamable HTTP transport (streamable_http_transport.rb:2413-2418) answer 500 server_error and call the exception reporter, instead of 401 invalid_token. Any client can send such a token. It needs no key.
jwt 3.3.0 fixed the root cause in jwt/ruby-jwt#722: Ps#verify now raises JWT::VerificationKeyError for a non-RSA key. That class is a JWT::DecodeError, so with jwt 3.3.0 the same token gives InvalidTokenError. But docs/installation.md asks for gem "jwt" with no version, so applications on jwt 3.2.0 and earlier, including the 2.x line, still get 500.
Repro
Run from the repository root with bundle exec ruby -I lib repro.rb:
require "mcp"
require "jwt"
require "openssl"
require "base64"
metadata = MCP::Server::OAuth::ProtectedResourceMetadata.new(
resource: "https://mcp.example.com/mcp",
authorization_servers: ["https://as.example.com"],
)
verifier = MCP::Server::OAuth::JWTVerifier.new(
resource_metadata: metadata,
key: OpenSSL::PKey::EC.generate("prime256v1"),
)
header = { alg: "PS256", typ: "JWT" }
claims = { iss: "https://as.example.com", aud: "https://mcp.example.com/mcp", exp: Time.now.to_i + 600 }
token = [header, claims].map { |part| Base64.urlsafe_encode64(JSON.generate(part), padding: false) }.join(".") + ".AAAA"
puts "jwt #{JWT::VERSION::STRING}"
begin
verifier.verify(token)
rescue => e
puts "#{e.class}: #{e.message}"
end
Expected (and the result with jwt 3.3.0):
jwt 3.3.0
MCP::Server::OAuth::InvalidTokenError: Invalid token
Actual with jwt 3.2.0:
jwt 3.2.0
NoMethodError: undefined method 'verify_pss' for an instance of OpenSSL::PKey::EC
Passing an explicit algorithms: ["ES256"] avoids the problem.
Suggested fix
Any of these:
- Drop the algorithms that do not fit the configured key before calling
JWT.decode:RS*andPS*for RSA keys,ES*for EC keys,EdDSAfor Ed25519 keys. This works with every jwt version. - Require jwt 3.3.0 or later when
JWTVerifierloads, with a clear error, and document the minimum version.
Add a regression test for each key source (key:, jwks:, jwks_uri:) through the middleware.
- Langage dominant
- Ruby
- Étoiles
- 940
- Forks
- 143
- Merge moyen
- 1 j 3 h
- PR mergées (30 j)
- 47
Préparer son environnement
- Aucun Dockerfile ni fichier Docker Compose
- Aucun modèle de pull request
- Lire le guide de contribution
Par où commencer
- Lisez l'issue en entier, puis le guide de contribution du projet.
- Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
- Forkez le dépôt et travaillez sur une branche.
- Ouvrez une pull request qui référence le numéro de l'issue.
Autres issues de modelcontextprotocol/ruby-sdk
-
Invalid UTF-8 in `MCP-Protocol-Version`, `Mcp-Method` or `Mcp-Name` gives 500 instead of 400Peut-être pris @atesgoral l’a pris aujourd’hui. Ouvertebug
Difficulté 2/5 1-3 heures Accessibilité débutants 78/100
modelcontextprotocol/ruby-sdk#663 ·
Les mainteneurs répondent en général sous 1 jour
-
`Client::Stdio` passes the whole parent environment to the server processPeut-être pris @atesgoral l’a pris aujourd’hui. Ouvertebug
Difficulté 4/5 3-5 jours Accessibilité débutants 55/100
modelcontextprotocol/ruby-sdk#662 ·
Les mainteneurs répondent en général sous 1 jour
-
Elicitation results are not checked against `requested_schema`, but the docs say they match itPeut-être pris @atesgoral l’a pris aujourd’hui. Ouvertebug
Difficulté 3/5 1-2 jours Accessibilité débutants 35/100
modelcontextprotocol/ruby-sdk#661 ·
Les mainteneurs répondent en général sous 1 jour
-
HTTP client sends handler exception messages back to the serverPeut-être pris @atesgoral l’a pris aujourd’hui. Ouvertebug
Difficulté 2/5 1-3 heures Accessibilité débutants 35/100
modelcontextprotocol/ruby-sdk#660 ·
Les mainteneurs répondent en général sous 1 jour
-
`logging/setLevel` from one session changes the log level of other sessionsPeut-être pris @atesgoral l’a pris aujourd’hui. Ouvertebug
Difficulté 2/5 1-3 heures Accessibilité débutants 35/100
modelcontextprotocol/ruby-sdk#658 ·
Les mainteneurs répondent en général sous 1 jour
Toutes les issues de modelcontextprotocol/ruby-sdk
Issues similaires
-
bug javascript
Difficulté 2/5 1-3 heures Accessibilité débutants 72/100
Les mainteneurs répondent en général sous 1 jour
-
Difficulté 2/5 1-3 heures Accessibilité débutants 88/100
yegor256/netbout.rb#69 ·
-
Difficulté 2/5 1-3 heures Accessibilité débutants 84/100
yegor256/iccq.github.io#161 ·
Les mainteneurs répondent en général sous 1 jour
-
Difficulté 2/5 1-3 heures Accessibilité débutants 68/100
palladius/rails8-app-on-gcp#198 ·
-
bug
Difficulté 1/5 Moins d'une heure Accessibilité débutants 95/100
WikiEducationFoundation/WikiEduDashboard#7258 ·
Les mainteneurs répondent en général sous 1 jour