From 8573bb9cc6c93d199b07505f3ee242046aa1ccc0 Mon Sep 17 00:00:00 2001 From: Kenneth Lien Date: Sun, 8 Dec 2024 17:25:47 -0800 Subject: [PATCH] Update spec --- docs/specification/auth/_index.md | 2 +- docs/specification/auth/credential.md | 56 ++++--- docs/specification/auth/oauth2.md | 226 +++++++++++--------------- 3 files changed, 132 insertions(+), 152 deletions(-) diff --git a/docs/specification/auth/_index.md b/docs/specification/auth/_index.md index 1ee0eeb..f501925 100644 --- a/docs/specification/auth/_index.md +++ b/docs/specification/auth/_index.md @@ -64,6 +64,7 @@ Servers that support authentication **MUST** include their supported authenticat "capabilities": { "auth": { "oauth2": { + "authorize": true, "token": true, "revoke": true }, @@ -103,7 +104,6 @@ Servers that support non-standard authentication schemes can declare them as exp 1. Clients **SHOULD**: - Prompt users for consent before initiating authentication flows - Provide clear user interfaces for authentication management - - Implement automatic token refresh - Handle authentication errors gracefully 2. Servers **SHOULD**: diff --git a/docs/specification/auth/credential.md b/docs/specification/auth/credential.md index eb9e03d..5b65c43 100644 --- a/docs/specification/auth/credential.md +++ b/docs/specification/auth/credential.md @@ -20,7 +20,7 @@ Clients supporting credential authentication **MUST** declare it during initiali { "capabilities": { "auth": { - "credential": true + "credentials": true } } } @@ -32,7 +32,7 @@ Servers supporting credentials **MUST** include their capabilities: { "capabilities": { "auth": { - "credential": { + "credentials": { "list": true } } @@ -43,31 +43,40 @@ Servers supporting credentials **MUST** include their capabilities: ## Protocol Messages ### Credential Requirements +Clients can list required credentials with an `auth/credentials/list` capability. -Servers can list required credentials using the credential/list capability: - +**Request:** +```json +{ + "jsonrpc": "2.0", + "id": 1, + "method": "auth/credentials/list", +} +``` +**Response:** ```json { "jsonrpc": "2.0", "id": 1, "result": { - "credential": [ + "credentials": [ { - "name": "X-API-KEY", + "name": "API-KEY", "description": "An API key must be provided to call this tool." }, { - "name": "X-MISC-PASSWORD", + "name": "MISC-PASSWORD", "description": "A password must be provided to list this resource" } ] } } ``` +- Clients and servers **MUST** treat credential names as case-insensitive. ### Providing Credentials -Clients provide credentials through headers during initialization: +Clients **MUST** provide credentials through headers during initialization: ```json { @@ -80,9 +89,11 @@ Clients provide credentials through headers during initialization: "credential": true } }, - "headers": { - "X-API-KEY": "api_key", - "X-MISC-PASSWORD": "password" + "auth": { + "credentials": { + "API-KEY": "api_key", + "MISC-PASSWORD": "password" + } } } } @@ -90,26 +101,33 @@ Clients provide credentials through headers during initialization: ## Error Handling -When credentials are missing or invalid, servers **SHOULD** respond with: +When credentials are missing or invalid, servers **MUST** respond with at least an error code. +**Response:** ```json { "jsonrpc": "2.0", "id": 1, "error": { - "code": 32004, - "message": "See required configuration", + "code": -32001, + "message": "Auth error, please see nested data.", "data": { - "requiredConfiguration": [ - { - "name": "X-API-KEY", - "description": "An API key must be provided to call this tool." + "authRequest": { + "credentials": { + "error": "ASCII error code", // REQUIRED + "errors": { // RECOMMENDED + // Breakdown of error per-credential + } + } - ] + } } } } ``` +- Servers **SHOULD** include helpful error messages +- Servers **SHOULD** include per-credential breakdowns of errors +- Clients **SHOULD** surface errors in a human-readable way to the end user. ## Security Considerations diff --git a/docs/specification/auth/oauth2.md b/docs/specification/auth/oauth2.md index c1360cb..5fca005 100644 --- a/docs/specification/auth/oauth2.md +++ b/docs/specification/auth/oauth2.md @@ -10,34 +10,37 @@ Auth is **experimental**, and being drafted for release in the next [revision]({ The additions to the base protocol are backwards compatible to revision 2024-11-05; however, **the auth specification may change in backwards incompatible ways** until the next protocol revision. {{< /callout >}} -The Model Context Protocol (MCP) supports OAuth 2.0 as a standardized authentication method, allowing secure authorization flows between clients and servers. Tokens will be securely communicated as part of the request body. +The Model Context Protocol (MCP) supports [OAuth 2.0](https://oauth.net/2/) as a standardized authentication method, allowing secure authorization flows between clients and servers. Tokens will be securely communicated as part of the request body. ## Protocol Flow +We follow the flows defined by [RFC 6749](https://datatracker.ietf.org/doc/html/rfc6749). ```mermaid sequenceDiagram - participant User participant Client as MCP Client + participant User as Resource Owner (End User) participant AuthServer as Authorization
MCP Server participant Resource as Resource
MCP Server - User->>Client: Initiate Login - Client->>AuthServer: Authorization Request - Note over Client,AuthServer: client_id, redirect_uri, scope, state - AuthServer->>User: Redirect to Login/Consent/Configuration - User->>AuthServer: Authenticate & Authorize - AuthServer->>Client: Authorization Code - Note over AuthServer,Client: Via redirect_uri with state - Client->>AuthServer: Token Request - Note over Client,AuthServer: auth code, client_id, client_secret - AuthServer->>Client: Access & Refresh Tokens - Client->>Resource: Access prompts/resources/tools - Resource->>Client: Access roots/sampling + Note right of Client: Obtaining an access token: + Client->>User: Authorization Request (auth/oauth2/authorize) + User->>Client: Authorization Grant + Client->>AuthServer: Authorization Grant (auth/oauth2/token) + AuthServer->>Client: Access Token + + Note right of Client: Using an access token: + Client->>Resource: Access prompts/resources/tools with Access Token + Resource->>Client: Receive protected response - Note right of Client: When token expires... - Client->>AuthServer: Refresh Token Request + + Note right of Client: Refreshing an access token: + Client->>AuthServer: Refresh Token Request (auth/oauth2/token) AuthServer->>Client: New Access Token + + Note right of client: Revoking tokens: + Client->>AuthServer: Revoke Token Request (auth/oauth2/revoke) ``` +Note that the authorization server may be the same server as the resource server or a separate entity. A single authorization server may issue access tokens accepted by multiple resource servers. ## Capabilities @@ -60,6 +63,7 @@ Servers supporting OAuth 2.0 **MUST** include their capabilities: "capabilities": { "auth": { "oauth2": { + "authorize": true, "token": true, "revoke": true } @@ -68,48 +72,57 @@ Servers supporting OAuth 2.0 **MUST** include their capabilities: } ``` -## Authentication Flows - -### Get Access Token Flow +## Flows +### Initialization +During initialization, if the client and server both support the `oauth2` capability, the client **SHOULD** include an access token in all subsequent requests. If the client does not have an access token, the client **SHOULD** obtain one. -1. Initial Request +### Obtaining an Access Token -When a client without valid credentials attempts to initialize, the server **MUST** respond with an OAuth 2.0 authorization requirement in the form of an error: +#### Authorization Grant Flow +To obtain an access token, clients **MUST** send an `auth/oauth2/authorize` request. +**Request:** ```json { "jsonrpc": "2.0", "id": 1, - "error": { - "code": -32001, - "message": "OAuth 2.0 authorization required", - "data": { - "authRequest": { - "authorizationUrl": ":///oauth2", - "clientId": "", - "scope": ["read", "write"], - "responseType": "code", - "state": "state" - } - } + "method": "auth/oauth2/authorize", + "params": { + "response_type": "code", // REQUIRED + "client_id": "client_id", // REQUIRED + "redirect_uri": "redirect_uri", // OPTIONAL + "scope": "scope", // OPTIONAL + "state": "state", // RECOMMENDED + } +} +``` +**Response:** +```json +{ + "jsonrpc": "2.0", + "id": 1, + "result": { + "authorization_url": ":///oauth2" } } ``` -The authorization URL **SHOULD** prompt the user to configure any credentials as needed. +- The client **MUST** redirect the user to the authorization URL. +- The authorization URL **SHOULD** prompt the user to configure any credentials as needed. Once complete, the server provides an authorization grant in the form of a code. +- The client **MUST** implement a way to receive the authorization grant. This can be through a callback such as the redirect URI, providing some interface for the user to provide it, etc. -2. Authorization Code Exchange +#### Access Token Exchange Flow After receiving the authorization code, the client **SHOULD** exchange it for tokens: ```json { "jsonrpc": "2.0", - "id": 2, + "id": 1, "method": "auth/oauth2/token", "params": { "grant_type": "authorization_code", - "code": "auth_code", - "state": "xyz" + "code": "code", + "state": "state" } } ``` @@ -121,29 +134,29 @@ The server responds with tokens: "jsonrpc": "2.0", "id": 2, "result": { - "access_token": "access_token", - "refresh_token": "refresh_token", - "expires_in": 3600 + "access_token": "access_token", // REQUIRED + "token_type": "token_type", // REQUIRED + "expires_in": 3600, // RECOMMENDED + "scope": "scope", // OPTIONAL if idential to client-requested scope, otherwise REQUIRED + "state": "state" // REQUIRED if the "state" parameter was present in the client authorization request. + // OPTIONAL additional parameters } } ``` +- The client **MUST** securely store the access token, and a refresh token if one is included. -### Valid Access Token Flow +### Utilizing an Access Token -Once a client has obtained an access token, it **SHOULD** include it in the initialization request: +Once a client has obtained an access token, it **SHOULD** include it in all requests in the parameters, including the initalization request. + +**Request:** ```json { "jsonrpc": "2.0", "id": 1, - "method": "initialize", + "method": "...", "params": { - "protocolVersion": "...", - "capabilities": { - "auth": { - "oauth2": true - } - }, "auth": { "oauth2": { "access_token": "..." @@ -153,33 +166,11 @@ Once a client has obtained an access token, it **SHOULD** include it in the init } ``` -### Invalid Access Token Flow +### Handling Expired Tokens -If the access token is invalid, the server **MUST** respond with: +If the client has a refresh token, the client **SHOULD** automatically request a new access token with a `auth/oauth2/token` request. If the client does not have a valid refresh token, it **SHOULD** obtain an access token as if the user was authenticating for the first time. -```json -{ - "jsonrpc": "2.0", - "id": 1, - "error": { - "code": -32002, - "message": "Descriptive OAuth 2.0 issue (invalid, expired, etc.)", - "data": { - "authRequest": { - "authorizationUrl": ":///oauth2", - "client_id": "client_id", - "scope": "scope", - "responseType": "code", - "state": "state" - } - } - } -} -``` - -### Refresh Token Flow - -When an access token expires, the client **SHOULD** request a new one using the refresh token: +**Request:** ```json { @@ -187,30 +178,17 @@ When an access token expires, the client **SHOULD** request a new one using the "id": 1, "method": "auth/oauth2/token", "params": { - "refresh_token": "refresh_token", - "grant_type": "refresh_token" - } -} -``` - -Server response: - -```json -{ - "jsonrpc": "2.0", - "id": 1, - "result": { - "accessToken": "access_token", - "refreshToken": "refresh_token", - "expiresIn": 3600, - "scope": "scope" + "refresh_token": "refresh_token", // REQUIRED + "grant_type": "refresh_token", // REQUIRED, must be set to `refresh_token` + "scope": "scope" // OPTIONAL } } ``` +The response will be identical to obtaining an access token for the first time. ### Revoke Token Flow -To revoke a token: +**Request:** ```json { @@ -224,66 +202,50 @@ To revoke a token: ``` ## Error Handling +### Error Responses +If a request failed client authentication or is invalid the server should respond with an error response as described in [Section 5.2 of RFC 6749](https://datatracker.ietf.org/doc/html/rfc6749#section-5.2). -Servers **SHOULD** return specific error codes for common OAuth 2.0 failure cases: - -- Invalid token: -32002 -- Expired token: -32002 -- Authorization required: -32001 - -Example error response: - +**Response:** ```json { "jsonrpc": "2.0", "id": 1, "error": { - "code": -32002, - "message": "Invalid OAuth 2.0 token", + "code": -32001, + "message": "Auth error, please see nested data.", "data": { "authRequest": { - "authorizationUrl": ":///oauth2", - "client_id": "client_id", - "scope": "scope", - "responseType": "code", - "state": "state" + "oauth2": { + "error": "ASCII error code from 5.2", // REQUIRED + "error_description": "Helpful message", // OPTIONAL + "error_uri": "Helpful webpage" // OPTIONAL + } } } } } ``` +Clients **SHOULD** handle errors as gracefully as possible with automated token refresh logic and presenting errors clearly. -## Implementation Requirements - -### Client Requirements -Clients **MUST**: -1. Support the OAuth 2.0 authorization code flow -2. Securely store access and refresh tokens -3. Include valid tokens in request headers -4. Implement token refresh logic -5. Handle token revocation - -### Server Requirements - -Servers **MUST**: -1. Validate all tokens -2. Provide clear error messages for authentication failures -3. Support token refresh -4. Implement token revocation -5. Maintain secure token storage +### Server Guidelines ## Security Considerations 1. Clients **MUST**: + - Follow security best practices outlined in the [OAuth 2.0 framework](https://datatracker.ietf.org/doc/html/rfc6749) - Securely store tokens - - Validate state parameters - - Implement proper token refresh logic - - Handle token revocation + - Provide clear user interfaces to token management, including obtaining and revoking tokens 2. Servers **MUST**: + - Follow security best practices outlined in the [OAuth 2.0 framework](https://datatracker.ietf.org/doc/html/rfc6749) + - Implement rate limiting for token endpoints - Validate all tokens - - Implement proper token expiration - Support token revocation - - Maintain secure storage of client secrets - - Implement rate limiting for token endpoints \ No newline at end of file + - Maintain secure storage of all secrets + +3. Servers **SHOULD**: + - Provide a UI that makes it easy for users to consent to and revoke access. + - Provide a secure way for clients to obtain client secrets for sensitive applications. + - Implement recommended/optional security features, such as scope, to limit client capabilities. + \ No newline at end of file