MCP Authorization With Dynamic Client Registration | Christian Posta<br>Post<br>Cancel<br>MCP Authorization With Dynamic Client Registration<br>Contents MCP Authorization With Dynamic Client Registration
This is a bonus post following on from my Understanding MCP Authorization three part series covering building (and understanding) an MCP HTTP based server and implementing the MCP Authorization spec (2025-06-18). In the previous series, we built the server side of the spec, leaving the client side up to the reader since obtaining OAuth clients is usually fairly opinionated in enterprise environments.<br>The MCP Authorization spec actually has opinions about how MCP clients (and thus, OAuth clients) should be created. The idea behind the spec is to allow MCP clients and servers to be “plug and play” automatically. That is, allow any MCP client to automatically discover what it needs to connect to an MCP server.<br>In this blog post, we implement an MCP client with Dynamic Client Registration for the OAuth client.<br>This series of blog posts (three parts + source code), walks “step-by-step” through the latest MCP Authorization spec and implement it. I have made all of the source code for each of the steps available on GitHub.<br>Part 1: Implement a spec compliant remote MCP server with HTTP TransportPart 2: Layer in Authorization specification with OAuth 2.1Part 3: Bring in a production Identity Provider (Keycloak)Follow (@christianposta or /in/ceposta) for the next parts.<br>Building an MCP Client<br>Follow along with the source code for this step.<br>The MCP client we build for this blog will focus on Dynamic Client Registration following RFC 7591. The process starts when an MCP client makes a request for a resource it is not authenticated for (HTTP 401). In that case, the MCP server would return a header WWW-Authenticate to help the MCP client figure out how to authenticate.<br>From the spec:<br>MCP servers MUST use the HTTP header WWW-Authenticate when returning a 401 Unauthorized to indicate the location of the resource server metadata URL as described in RFC9728 Section 5.1 “WWW-Authenticate Response”.
MCP clients MUST be able to parse WWW-Authenticate headers and respond appropriately to HTTP 401 Unauthorized responses from the MCP server.<br>In our implementation in step10, we built the MCP server to return a 401 and Header:
WWW-Authenticate: Bearer realm="mcp-server", resource_metadata="http://localhost:9000/.well-known/oauth-protected-resource"
Note this part in the value of the header: resource_metadata=”http://localhost:9000/.well-known/oauth-protected-resource.<br>The MCP client will then request the oauth-protected-resource metadata (following RFC 9728).<br>In our MCP server, it looks like this:
10<br>11<br>12<br>13<br>14<br>"resource": "http://localhost:9000",<br>"authorization_servers": ["http://localhost:8080/realms/mcp-realm"],<br>"scopes_supported": [<br>"echo-mcp-server-audience",<br>"mcp:read",<br>"mcp:tools",<br>"mcp:prompts"<br>],<br>"bearer_methods_supported": ["header"],<br>"resource_documentation": "http://localhost:9000/docs",<br>"mcp_protocol_version": "2025-06-18",<br>"resource_type": "mcp-server"
A number of interesting points here. The authorization_servers is where the client should look for how to connect to the Authorization Server (AS). The scopes_supported is how the MCP server tells its clients what scopes it will need to make calls. Interestingly this will be ALL of the scopes needed to access all parts of the MCP server. When the MCP client registers an OAuth client, it should use these scopes. It’s on the AS to determine which users have which roles and what scopes will actually appear in their tokens. When the MCP client initiates an authorization code flow, it should use these scopes.<br>The next step is to call the Authorization Server’s (AS) metadata resource based on what was in the authorization_servers list. The MCP client will append /.well-known/oauth-authorization-server to the value of authorization_servers to discover the endpoints for authorization, client registration, etc on the Authorization Server (AS). In this case, the metadata is located here:
http://localhost:8080/realms/mcp-realm/.well-known/oauth-authorization-server
In our example, since we are using Keycloak, you’ll note the /realms/ here. Other Authorization Servers will be different. When our MCP client calls this and parses the AS metadata, the MCP client should now have enough information to initiate a Dynamic Client Registration and proceed to OAuth flows:<br>The relevant endpoints in our example:
token_endpoint: http://localhost:8080/realms/mcp-realm/protocol/openid-connect/token<br>authorization_endpoint: http://localhost:8080/realms/mcp-realm/protocol/openid-connect/auth<br>registration_endpoint: http://localhost:8080/realms/mcp-realm/clients-registrations/openid-connect
Based on that response, we see that the registration_endpoint is: http://localhost:8080/realms/mcp-realm/clients-registrations/openid-connect. Now our MCP client should make a call to the...