keycardai-mcp 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e7fc6bb3654a0b2a8e00298d2d36cd551786982bbae2404600a5d9a8d01b479f
4
- data.tar.gz: 34c7ed0e14be12c111614ab20c7667ef5de9c2882873fbb03c1c8d951dcf5791
3
+ metadata.gz: '0880d377eb3029f73175e4ba00d78bca2c6bbdaf0c7ace5a8012efc44416033e'
4
+ data.tar.gz: 73ba72fadb3d1f534847f171f46fed3c5db52744521f45658e9c1960979e4f21
5
5
  SHA512:
6
- metadata.gz: deefc119bca708aff1721536932f0fc9b6c09ab463e0c2c980e8702c8fec3eced345b59473956ff4cd873358d8f59ea0c4e24059fe7edb36b61e99d1c953548a
7
- data.tar.gz: f6c15892df3b646cddbc87c6f5c02f328b4da569f190b8c54da22e4a145a3b6d20780cc0d878c662148b68cfd38eba2aba657532e7e4cd7d8fcf228b1a064959
6
+ metadata.gz: daf1d161bb279219b0c7abc1c2bb801fb6f7e77507b64309425722bb6449e8ca76b2191115a9571b3db1cfd50155e7ae3ec85994ab5bac2659906520d68b814a
7
+ data.tar.gz: 104a2890048bfbda6935b39339946a82722e6c918dccda8b6f699aba27e8cea98bd243eae8b9d76a37e25f68b8a116dfab58c5f14f251bd033436a32582b5b01
data/CHANGELOG.md CHANGED
@@ -14,6 +14,12 @@ protected-resource with path insertion, an RFC 8414 authorization-server proxy,
14
14
  and JWKS), and an AuthProvider whose `grant` middleware exchanges the caller's
15
15
  token per resource into an AccessContext. Wraps no MCP SDK.
16
16
 
17
+ ## 0.1.2-keycardai-mcp (2026-10-12)
18
+
19
+
20
+ - fix(keycardai-mcp): RequireBearerAuth answers 503 when the zone's signing keys can't be fetched (SDK-20) (#44)
21
+ - RequireBearerAuth answers 503 {"error":"temporarily_unavailable"} with no challenge when verification fails with JWKSFetchError, JWKSDiscoveryError, HTTPError, or NetworkError, because the token was never judged. A kid absent from a JWKS that loaded, and any other invalid token, stays 401 invalid_token.
22
+
17
23
  ## 0.1.1-keycardai-mcp (2026-09-16)
18
24
 
19
25
 
data/README.md CHANGED
@@ -31,8 +31,11 @@ is proven by the example server in the repo's `examples/` directory.
31
31
  require "keycardai/mcp"
32
32
 
33
33
  zone_url = ENV.fetch("KEYCARD_URL")
34
+ # This server's registered Resource identifier. Leaving audiences: unset
35
+ # accepts tokens minted for any resource in the zone and warns once at boot.
36
+ resource_id = ENV.fetch("KEYCARD_RESOURCE_ID")
34
37
 
35
- verifier = Keycardai::OAuth::TokenVerifier.new(issuers: zone_url)
38
+ verifier = Keycardai::OAuth::TokenVerifier.new(issuers: zone_url, audiences: resource_id)
36
39
  metadata = Keycardai::MCP::MetadataApp.new(
37
40
  issuer: zone_url,
38
41
  resource_name: "mcp-server-ruby",
@@ -9,7 +9,7 @@ module Keycardai
9
9
  # the Rack env (Keycardai::MCP.auth_info).
10
10
  #
11
11
  # use Keycardai::MCP::RequireBearerAuth,
12
- # verifier: Keycardai::OAuth::TokenVerifier.new(issuers: zone_url),
12
+ # verifier: Keycardai::OAuth::TokenVerifier.new(issuers: zone_url, audiences: resource_id),
13
13
  # required_scopes: ["mcp:tools"]
14
14
  #
15
15
  # Route-level gating is this same middleware applied per route with a
@@ -49,6 +49,9 @@ module Keycardai
49
49
 
50
50
  def authorize(env, token)
51
51
  access_token, failure = verify(token)
52
+ if failure == :unavailable
53
+ return RackSupport.json_response({ "error" => "temporarily_unavailable" }, status: 503)
54
+ end
52
55
  return RackSupport.challenge_response(env, status: 401, error: "invalid_token", description: failure) if failure
53
56
 
54
57
  missing = @required_scopes - access_token.scopes
@@ -61,8 +64,21 @@ module Keycardai
61
64
  @app.call(env)
62
65
  end
63
66
 
67
+ # The zone's signing keys or its authorization-server metadata could not
68
+ # be fetched, so verification never completed. Answered 503 with no
69
+ # challenge: the token was not judged, and the client should retry.
70
+ # A kid absent from a JWKS that did load (JWKSKeyNotFoundError) is an
71
+ # invalid token and keeps the 401 path.
72
+ UNAVAILABLE_ERRORS = [
73
+ Keycardai::OAuth::JWKSFetchError, Keycardai::OAuth::JWKSDiscoveryError,
74
+ Keycardai::OAuth::HTTPError, Keycardai::OAuth::NetworkError
75
+ ].freeze
76
+ private_constant :UNAVAILABLE_ERRORS
77
+
64
78
  def verify(token)
65
79
  [@verifier.verify_token(token), nil]
80
+ rescue *UNAVAILABLE_ERRORS
81
+ [nil, :unavailable]
66
82
  rescue Keycardai::Error => e
67
83
  [nil, e.message]
68
84
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Keycardai
4
4
  module MCP
5
- VERSION = "0.1.1"
5
+ VERSION = "0.1.2"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: keycardai-mcp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Keycard