Lion Cipher Lab
MCP server, Part 4: dynamic client registration and PKCE — let unknown clients register themselves and get tokens with no shared secret (all local, proven with curl)
- AI Security
- Advanced
- ~60 min
- Free · no account required
Why it matters
Part 3 proved your MCP server can validate a real token, but it quietly assumed you had already hand-registered the one client that would call it and were minting its tokens yourself. That does not survive contact with the real MCP world. The MCP authorization model expects an open ecosystem: an AI host or agent you have never met should be able to DISCOVER your server, obtain its own credentials, and get a token WITHOUT you registering each client by hand — that is Dynamic Client Registration (RFC 7591). And those clients are usually public: a desktop app, a CLI agent, a single-page tool…
What you'll learn
- standing up a local OAuth 2.1 authorization server stand-in (registration + authorize + token endpoints)
- server discovery: Protected Resource Metadata (RFC 9728) then Authorization Server Metadata (RFC 8414)
- Dynamic Client Registration (RFC 7591): letting an unknown client obtain a client_id with no hand-registration
- public clients with no secret (token_endpoint_auth_method: none) and why they must use PKCE
- PKCE (RFC 7636): deriving code_challenge = BASE64URL(SHA256(code_verifier)) and proving the verifier at /token
- demonstrating the code-interception defense: a stolen code is rejected (invalid_grant) without the verifier
- judging when DCR + PKCE is worth it versus Part 3's static confidential client, and the abuse it invites
Steps in this lab
- What Part 3 left open — the pre-registered, self-minted client
- The two new mechanisms: Dynamic Client Registration and PKCE (and how discovery finds them)
- When DCR + PKCE is worth it — and when Part 3's static client is the right call
- Set up: reuse the Part 3 folder and start the (unchanged) resource server
- Build the local authorization server stand-in: as.py
- Discover the endpoints with curl: protected-resource metadata, then AS metadata
- Register dynamically (RFC 7591): get a client_id with no hand-registration
- PKCE (RFC 7636): make a verifier + challenge, get a code, exchange it for a token
- Watch the interception defense fire: replay a stolen code without the verifier
- Failure modes and doing it better: gate DCR, keep redirect-URI + state, use a real AS