OAuth for MCP — Solved exercises
Curso MCP Engineering · M3
Conceptual exercises to solidify the OAuth 2.1 flow in remote MCP servers. There's no need to write code: it's about understanding the journey and why each step is where it is. Solve them on your own first; each exercise has a detailed solution further down.
Remember the map before you begin: 401 → discovery → token → validate. The client asks without credentials, the server responds401with a clue (WWW-Authenticate), the client does discovery, gets a JWT from the authorization server, and retries withAuthorization: Bearer <token>; the server validates issuer, audience, and scope.
Exercise 1 — Order the flow
These five moments are out of order. Number them 1 to 5 in the correct order:
- ( ) The client retries with
Authorization: Bearer <token>. - ( ) The server responds
401 UnauthorizedwithWWW-Authenticate. - ( ) The client sends the first request, without credentials.
- ( ) The server validates the JWT (issuer, audience, scope).
- ( ) The client does discovery and obtains the token from the authorization server.
Exercise 2 — The missing step
A developer describes their flow like this: "The client asks, receives a 401, and then the client retries the same request expecting to be let through the second time." What essential step did they skip? Why will the retry, as described, not work?
Exercise 3 — Open discovery
The /.well-known/oauth-protected-resource endpoint responds without asking for credentials. A colleague says: "That's a security hole: anyone can query it." Are they right? Explain why that endpoint must stay open and what information it hands out (and what it does not).
Exercise 4 — Validating the JWT
Your server receives a request with a Bearer <token>. The token is correctly signed and hasn't expired. Even so, your server rejects it. Name two reasons why a well-signed and valid token can, correctly, be rejected. (Hint: think about the three fields the server validates.)
Exercise 5 — Local or remote?
For each case, decide whether it needs OAuth 2.1 (yes / no) and justify it in one line:
- A. An MCP server that runs on your laptop over stdio, used only by you.
- B. An MCP server published on the internet over HTTP, reachable by several clients.
- C. A remote MCP server meant to be public, with no private data, for anyone.
Exercise 6 — Translate the header
A client receives this response from the server:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://api.ejemplo.com/.well-known/oauth-protected-resource"
What should the client do next, and why did the server send it that URL instead of simply saying "no"?
Exercise 7 — The stolen token from next door
Your MCP server is api.miapp.com. An attacker got hold of a legitimate and valid JWT that another service, api.otracosa.com, issued to a real user. The attacker presents it at your server with Authorization: Bearer <that-token>. Which JWT field stops this attack, and how?
Exercise 8 — Who issues and who validates?
Complete the sentence with the correct actors (client, MCP server, authorization server): "The ______ issues the JWT; the ______ presents the JWT on each request; the ______ validates the JWT before responding." Then, explain why the MCP server can validate the token without calling the authorization server on every request.
Solutions
1 — Correct order.
- The client sends the first request, without credentials.
- The server responds
401 UnauthorizedwithWWW-Authenticate. - The client does discovery and obtains the token from the authorization server.
- The client retries with
Authorization: Bearer <token>. - The server validates the JWT (issuer, audience, scope).
Notice that discovery and obtaining the token live together in step 3: they're the bridge between receiving the 401 and being able to retry with something in hand.
2 — The missing step. Between the 401 and the retry, the client has to obtain a token. The 401 does not mean "try again": it means "you need credentials, and here's the clue (WWW-Authenticate) to get them." Without doing discovery and obtaining the JWT from the authorization server, the retry arrives just as empty and gets 401 again. Repeating the same request without credentials is repeating the same mistake: nothing changed between the first attempt and the second.
3 — Open discovery. The colleague is not right. That endpoint must stay open precisely so a new client can get started (bootstrap) without knowing anything in advance. It only hands out metadata about where to ask for the token (who the authorization server is) — it does not hand out protected data or tokens. Knowing which door to knock on doesn't let you in: for that you still need a valid JWT. It's like having the "reception, ground floor" sign: it tells you where to go, but it doesn't open any office for you.
4 — Rejection of a signed, valid token. Either of these two reasons is enough:
- Wrong audience (
aud): the token was issued for another server (another service), not for yours. A token valid elsewhere isn't valid here. - Insufficient scope: the token doesn't include the permission needed for the action being requested (for example, it has read permission but a write was requested).
- (Also valid: a different issuer from the authorization server your server trusts.)
The correct signature and validity are necessary but not sufficient: they say "this token is real and hasn't expired," not "this token works here and for this."
5 — Local or remote.
- A. No. It runs locally over stdio and only you use it: whoever reaches your machine is already inside.
- B. Yes. It's a remote server reachable from the internet; if it's going to control access, OAuth 2.1 is the standard MCP requires to decide who can use it.
- C. No (optional). Authenticating is optional: a remote server meant to be public may choose not to authenticate anyone. The rule doesn't say "every remote server MUST authenticate"; it says "if it authenticates, it must be with OAuth 2.1." A public server simply chooses not to.
6 — Translate the header. The client should follow that discovery URL (/.well-known/oauth-protected-resource) to find out which authorization server to ask for the token, obtain the JWT, and retry the original request with Authorization: Bearer <token>. The server sends the URL instead of a curt "no" because the 401 is designed to be actionable: it gives the client the map to get permission on its own, without human intervention. A bare "no" would leave the client not knowing where to go.
7 — The stolen token from next door. The audience (aud) field stops it. That token was issued for api.otracosa.com, so its aud points to that service. When it reaches your server, your validation asks "was this token issued for me?" and the answer is no → you reject it. That's why validating aud is not optional: without that check, any legitimate token from any service would work in yours. The correct signature isn't enough: the token is real, but it's not for you.
8 — Who issues and who validates. "The authorization server issues the JWT; the client presents the JWT on each request; the MCP server validates the JWT before responding." The MCP server can validate without calling the authorization server because the JWT is self-verifiable: it carries the information inside and a signature that the server checks with the authorization server's public key. Like an event wristband: the security guard looks at the seal and decides on the spot, without phoning the organization. That makes validation fast and means the server doesn't have to keep a copy of every issued token.
To remember
- The flow is always 401 → discovery → token → validate.
- A
401asks you to get credentials, not to repeat the empty request. - Discovery stays open on purpose: it hands out the map, not the key.
- Correct signature + not expired isn't enough: the server validates issuer, audience, and scope.
- Audience is what keeps a token from another service from working in yours.
- The JWT is self-verifiable: the server validates with the signature, without calling anyone.
Turning Point Academy · MCP Engineering Course · Module 3 · OAuth exercises