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 +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +4 -1
- data/lib/keycardai/mcp/require_bearer_auth.rb +17 -1
- data/lib/keycardai/mcp/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '0880d377eb3029f73175e4ba00d78bca2c6bbdaf0c7ace5a8012efc44416033e'
|
|
4
|
+
data.tar.gz: 73ba72fadb3d1f534847f171f46fed3c5db52744521f45658e9c1960979e4f21
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|