Skip to content

Commit 1801a5d

Browse files
committed
refactor: update scope hints and improve README clarity across exercises
1 parent e3a7c38 commit 1801a5d

File tree

5 files changed

+23
-47
lines changed

5 files changed

+23
-47
lines changed

exercises/05.scopes/01.problem.check-scopes/README.mdx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,10 @@ Once you have the scope validation utilities in place, you'll also need to add a
3838
This approach means users will only see the tools and features they're actually authorized to use, creating a smooth and intuitive experience that matches their permission level.
3939

4040
📜 For more details on OAuth scopes and how they work in MCP servers, see the [OAuth 2.0 Scopes RFC](https://tools.ietf.org/html/rfc6749#section-3.3) and the [MCP Authentication Specification](https://modelcontextprotocol.io/specification/2025-06-18/server/auth).
41+
42+
<callout-danger>
43+
At the time of this writing, there is a bug in the Cloudflare `McpAgent` that
44+
prevents client capabilities from being provided properly, as a result,
45+
sampling requests will not be made. Add `console.log`s to check if your logic
46+
is working.
47+
</callout-danger>

exercises/05.scopes/03.problem.scope-hints/README.mdx

Lines changed: 4 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,11 @@
22

33
👨‍💼 When users try to access our EpicMe journaling app without proper permissions, they need clear guidance on what scopes are available and required. Without this information, clients can't know what to request during the OAuth authorization flow, leading to failed authentication attempts and frustrated users.
44

5-
The solution is to provide **scope hints** in our OAuth responses. These hints tell clients exactly what scopes are supported and help them understand what permissions they need to request from the authorization server.
5+
The solution is to provide a **scope hint** (called `scopes_supported`) in our OAuth protected resource metadata. This metadata tells clients exactly what scopes are supported and help them understand what permissions they could request from the authorization server.
66

77
Here's how this works in practice. Imagine a smart home app that controls different devices:
88

9-
```ts lines=8,20-25
10-
// When a client gets a 401 Unauthorized response, we include scope hints
11-
function handleUnauthorized(request: Request) {
12-
return new Response('Unauthorized', {
13-
status: 401,
14-
headers: {
15-
'WWW-Authenticate': [
16-
'Bearer realm="SmartHome"',
17-
'scope="lights:read lights:write thermostat:read security:admin"',
18-
'resource_metadata=https://smarthome.example.com/.well-known/oauth-protected-resource',
19-
].join(', '),
20-
},
21-
})
22-
}
23-
9+
```ts lines=6-11
2410
// The protected resource metadata also lists supported scopes
2511
function handleOAuthProtectedResourceRequest(request: Request) {
2612
return Response.json({
@@ -36,25 +22,8 @@ function handleOAuthProtectedResourceRequest(request: Request) {
3622
}
3723
```
3824

39-
The `scope` parameter in the `WWW-Authenticate` header tells the client what scopes are available for this specific resource. The `scopes_supported` in the protected resource metadata provides a complete list of all supported scopes across the entire system.
40-
41-
<callout-info>
42-
The `scope` parameter in OAuth error responses helps clients understand what
43-
permissions they can request, while `scopes_supported` in the metadata
44-
endpoint gives them the complete picture of available scopes.
45-
</callout-info>
46-
47-
<callout-warning>
48-
Note the difference between 401 and 403 responses: In a **401 Unauthorized**
49-
response, the `scope` parameter lists **all available scopes** for discovery
50-
("here's what you can request"). In a **403 Forbidden** response, the `scopes`
51-
parameter would list **required scopes** for that specific request ("here's
52-
what you need"). We can't use `scopes` in our 403 response because we have
53-
multiple valid scope combinations rather than a single required set.
54-
</callout-warning>
55-
56-
This approach ensures that when users encounter permission issues, their client apps can automatically request the right scopes and guide users through a smooth authorization flow.
25+
The `scopes_supported` in the protected resource metadata provides a complete list of all supported scopes across the entire system.
5726

5827
📜 For more details on OAuth scope parameters, see the [OAuth 2.0 Authorization Framework RFC](https://tools.ietf.org/html/rfc6749#section-3.3).
5928

60-
Now, add the missing scope hints to help clients understand what permissions are available for our EpicMe journaling app.
29+
Now, add the missing `scopes_supported` to help clients understand what permissions are available for our EpicMe journaling app.

exercises/05.scopes/03.problem.scope-hints/src/auth.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ export function handleUnauthorized(request: Request) {
112112
? `error_description="The access token is invalid or expired"`
113113
: null,
114114
`resource_metadata=${url.toString()}`,
115-
// 🐨 add a scope hint for the supported scopes (join the supported scopes with a space)
115+
// No scope hint here for the same reason as above
116116
]
117117
.filter(Boolean)
118118
.join(', '),

exercises/05.scopes/03.solution.scope-hints/src/auth.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ export function handleUnauthorized(request: Request) {
112112
? `error_description="The access token is invalid or expired"`
113113
: null,
114114
`resource_metadata=${url.toString()}`,
115-
`scope=${supportedScopes.join(' ')}`,
115+
// No scope hint here for the same reason as above
116116
]
117117
.filter(Boolean)
118118
.join(', '),

exercises/05.scopes/README.mdx

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -45,20 +45,20 @@ Here's a sequence diagram showing how scope validation works in an MCP server:
4545
```mermaid
4646
sequenceDiagram
4747
participant Client
48-
participant MCP_Server
49-
participant Auth_Server
48+
participant MCP Server
49+
participant Auth Server
5050
participant Database
5151
52-
Client->>MCP_Server: Request with access token
53-
MCP_Server->>Auth_Server: Introspect token
54-
Auth_Server-->>MCP_Server: Token info + scopes
55-
MCP_Server->>MCP_Server: Validate required scopes
52+
Client->>MCP Server: Request with access token
53+
MCP Server->>Auth Server: Introspect token
54+
Auth Server-->>MCP Server: Token info + scopes
55+
MCP Server->>MCP Server: Validate required scopes
5656
alt Sufficient scopes
57-
MCP_Server->>Database: Execute requested operation
58-
Database-->>MCP_Server: Return data
59-
MCP_Server-->>Client: Success response
57+
MCP Server->>Database: Execute requested operation
58+
Database-->>MCP Server: Return data
59+
MCP Server-->>Client: Success response
6060
else Insufficient scopes
61-
MCP_Server-->>Client: 403 Forbidden with scope hints
61+
MCP Server-->>Client: 403 Forbidden with scope hints
6262
end
6363
```
6464

0 commit comments

Comments
 (0)