akanjs 3.0.0-alpha.69 → 3.0.0-alpha.70

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "akanjs",
3
- "version": "3.0.0-alpha.69",
3
+ "version": "3.0.0-alpha.70",
4
4
  "sourceType": "module",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -21,8 +21,9 @@ interface McpAuthFailure {
21
21
  }
22
22
 
23
23
  /**
24
- * The OAuth 2.1 protected-resource half of the MCP authorization spec: the RFC 9728 metadata document, the
25
- * `WWW-Authenticate` challenge that points at it, and the token checks that can be made without an issuer.
24
+ * The OAuth 2.1 protected-resource half of the MCP authorization spec: the RFC 9728 metadata document (served
25
+ * only once an authorization server is named), the `WWW-Authenticate` challenge that points at it, and the
26
+ * token checks that can be made without an issuer.
26
27
  *
27
28
  * The authorization server itself is a separate project. What lives here is what a resource server owes a
28
29
  * client regardless of who mints the tokens, so wiring one up later needs no change on this side.
@@ -39,8 +40,12 @@ export class McpAuth {
39
40
  /**
40
41
  * Both spellings of the metadata path. RFC 9728 inserts the resource's path into the well-known URL, and MCP
41
42
  * clients try that form first and the bare one second; serving only one leaves half the clients at a 404.
43
+ *
44
+ * Absent until an issuer is named: an unprotected resource that still advertises `bearer_methods_supported`
45
+ * is treated by Cursor (and similar clients) as an OAuth mandate.
42
46
  */
43
47
  createRoutes(): HttpRoutes {
48
+ if (!this.#hasIssuer()) return {};
44
49
  const handler = { GET: (req: Request) => this.#metadata(req) };
45
50
  return {
46
51
  [McpAuth.wellKnownPath]: handler,
@@ -84,7 +89,7 @@ export class McpAuth {
84
89
  (value): value is string => typeof value === "string",
85
90
  );
86
91
 
87
- if (this.#props.authorizationServers?.length && !audience.length)
92
+ if (this.#hasIssuer() && !audience.length)
88
93
  return { status: 401, error: "invalid_token", description: "The access token names no resource." };
89
94
  if (audience.length && !audience.includes(this.#resource(req)))
90
95
  return { status: 401, error: "invalid_token", description: "The access token was issued for another resource." };
@@ -128,7 +133,9 @@ export class McpAuth {
128
133
  return [
129
134
  "Bearer",
130
135
  [
131
- `resource_metadata="${new URL(McpAuth.wellKnownPath + this.#props.path, McpAuth.origin(req)).href}"`,
136
+ ...(this.#hasIssuer()
137
+ ? [`resource_metadata="${new URL(McpAuth.wellKnownPath + this.#props.path, McpAuth.origin(req)).href}"`]
138
+ : []),
132
139
  `error="${error}"`,
133
140
  `error_description="${description}"`,
134
141
  ...(scopes.length ? [`scope="${scopes.join(" ")}"`] : []),
@@ -136,6 +143,10 @@ export class McpAuth {
136
143
  ].join(" ");
137
144
  }
138
145
 
146
+ #hasIssuer() {
147
+ return Boolean(this.#props.authorizationServers?.length);
148
+ }
149
+
139
150
  #resource(req: Request) {
140
151
  return this.#props.resource ?? new URL(this.#props.path, McpAuth.origin(req)).href;
141
152
  }
@@ -17,8 +17,9 @@ interface McpAuthFailure {
17
17
  description: string;
18
18
  }
19
19
  /**
20
- * The OAuth 2.1 protected-resource half of the MCP authorization spec: the RFC 9728 metadata document, the
21
- * `WWW-Authenticate` challenge that points at it, and the token checks that can be made without an issuer.
20
+ * The OAuth 2.1 protected-resource half of the MCP authorization spec: the RFC 9728 metadata document (served
21
+ * only once an authorization server is named), the `WWW-Authenticate` challenge that points at it, and the
22
+ * token checks that can be made without an issuer.
22
23
  *
23
24
  * The authorization server itself is a separate project. What lives here is what a resource server owes a
24
25
  * client regardless of who mints the tokens, so wiring one up later needs no change on this side.
@@ -30,6 +31,9 @@ export declare class McpAuth {
30
31
  /**
31
32
  * Both spellings of the metadata path. RFC 9728 inserts the resource's path into the well-known URL, and MCP
32
33
  * clients try that form first and the bare one second; serving only one leaves half the clients at a 404.
34
+ *
35
+ * Absent until an issuer is named: an unprotected resource that still advertises `bearer_methods_supported`
36
+ * is treated by Cursor (and similar clients) as an OAuth mandate.
33
37
  */
34
38
  createRoutes(): HttpRoutes;
35
39
  unauthorized(req: Request, failure?: McpAuthFailure): Response;