Fix token endpoint errors
This check verifies that your token_endpoint exchanges the authorization code for a Bearer access
token.
Check id token_exchange (token exchange). Results:
Why it matters
Section titled “Why it matters”The token exchange is the last step of sign-in. The user has signed in and approved access, and the agent now trades the code for the token it will send to your MCP server. If the exchange fails, the user sees a sign-in error right after a successful login, which is the hardest failure for them to understand.
- The MCP authorization specification requires clients to send the
resourceparameter (RFC 8707) in both the authorization request and the token request, naming the MCP server, whether or not the authorization server supports it. - ChatGPT appends
resourceto both requests and expects it to tie the token to your protected resource metadata (OpenAI: Authentication). - Claude sends the exchange as
application/x-www-form-urlencodedand waits up to 10 seconds for your token endpoint to answer (Claude: Authentication for connectors).
So a token endpoint that rejects resource, or parses only JSON, fails for every agent that follows
the specification.
What MCP Tower checks
Section titled “What MCP Tower checks”This check runs only while someone signs in with Sign in to scan. In a scan it shows “Checked when someone signs in.” It also runs only after the authorization check passed; otherwise it is skipped.
- MCP Tower sends a
POSTtotoken_endpointwith a form body:grant_type=authorization_code, thecode, theredirect_uriit used, the PKCEcode_verifier, andresourceset to your resource identifier. - The client authenticates the way it registered: HTTP Basic for
client_secret_basic,client_idandclient_secretin the body forclient_secret_post, and onlyclient_idfornone, which public clients such as a Client ID Metadata Document client use. - An error response is a
fail. The message quotes the HTTP status and the OAutherroranderror_description, and the next step depends on the code. A connection error is afailtoo. - A successful response must carry an access token whose
token_typeisBearer. A response withouttoken_typecounts as Bearer. Any other type is afail.
Results
Section titled “Results”| Status | Message | Meaning | Fix |
|---|---|---|---|
| pass | The token endpoint returned a Bearer access token. | Sign-in is complete. The next check is token accepted. | |
| fail | The token exchange failed with HTTP <status>: invalid_grant (<description>). | Next step: “The authorization code or verifier was refused. Sign in again; if it repeats, check the provider’s PKCE support and code lifetime.” | Accept the code |
| fail | The token exchange failed with HTTP <status>: invalid_client (<description>). | Next step: “The provider refused the client credentials. Check the client id and secret, or reset the connection to register again.” | Accept the client |
| fail | The token exchange failed with HTTP <status>: invalid_target (<description>). | Next step: “Support RFC 8707 resource indicators: accept the resource parameter naming this MCP server.” | Accept resource |
| fail | The token exchange failed with HTTP <status>: unauthorized_client (<description>). | Next step: “The provider does not allow this client to use the authorization code flow. Enable it for the client.” | Accept the client |
| fail | The token exchange failed with HTTP <status>: invalid_scope (<description>). | Next step: “The provider rejected a requested scope. Check the scopes the server declares against those the provider allows.” | Tool scopes |
| fail | The token exchange failed with HTTP <status>: <error> (<description>). | Any other code, such as invalid_request or unsupported_grant_type. Next step: “Check the provider’s logs for this error and the client’s configuration.” | Accept the request |
| fail | The token exchange failed with HTTP <status>. | An error status without an OAuth error. Same next step as above. | Accept the request |
| fail | The token exchange failed: the token endpoint could not be reached (<error class>). | dns_error, connect_error, tls_error, timeout or address_blocked. Next step: “Check that token_endpoint is reachable over https.” | Accept the request |
| fail | The token endpoint returned a <type> token, not a Bearer token. | For example MAC. Next step: “Issue Bearer access tokens for this client.” | Issue Bearer tokens |
| skipped | Checked when someone signs in. | A scan, not a sign-in. | Select Sign in to scan |
| skipped | Needs a valid callback. | The callback’s state did not match. | Authorization |
| skipped | Needs an authorization code. | The callback carried an error, or no code. | Authorization |
| skipped | The callback was rejected. | The callback’s iss did not match the issuer. | Authorization |
The description is cut after 200 characters. The message never contains the code, the verifier, the token or the response body.
How to fix
Section titled “How to fix”Accept the code and verifier
Section titled “Accept the code and verifier”invalid_grant means the code is unknown, expired, already used, issued to another client or
redirect URI, or the code_verifier does not hash to the code_challenge with S256.
- Check that codes live long enough. One minute is common; the exchange follows the redirect within seconds.
- Check that the code is bound to the same
client_idandredirect_urithe token request sends. - Check that the endpoint verifies
S256: base64url of the SHA-256 of the verifier, without padding (RFC 7636 §4.6).
Accept the client
Section titled “Accept the client”invalid_client means the credentials did not match the client’s registered authentication
method. If the client was created by Dynamic Client Registration, its secret is stored per user;
select Reset my connection on the server’s page and sign in again to register a new one. For
credentials an Admin entered, check the client id and secret. unauthorized_client means the
client may not use the authorization_code grant: enable it for the client.
Accept the resource parameter
Section titled “Accept the resource parameter”Your token endpoint must accept resource, and should issue a token whose audience is that URL
(RFC 8707 §2). Rejecting it with
invalid_target blocks every agent, because MCP clients send it in every token request. Allow the
URL in your protected resource metadata’s resource as a valid target. When MCP Tower runs in
front of your server, that is the public hostname URL; see
public audience.
Accept the request format
Section titled “Accept the request format”The token request is application/x-www-form-urlencoded
(RFC 6749 §4.1.3), not JSON. Errors
come back as JSON with error and error_description
(RFC 6749 §5.2), so agents can show
what went wrong. The endpoint must be an https URL that answers from the public internet.
Issue Bearer tokens
Section titled “Issue Bearer tokens”A successful response names the token type Bearer
(RFC 6750 §4), because MCP clients send
the token in an Authorization: Bearer header:
HTTP/1.1 200 OKContent-Type: application/jsonCache-Control: no-store
{ "access_token": "<access-token>", "token_type": "Bearer", "expires_in": 3600, "refresh_token": "<refresh-token>", "scope": "mcp:tools"}Check it yourself
Section titled “Check it yourself”Send a token request with a made-up code. Use the client id from the last registration and its redirect URI:
curl -si https://auth.example.com/token \ -d grant_type=authorization_code \ -d code=made-up-code \ -d client_id='<client-id>' \ --data-urlencode redirect_uri='<redirect-uri>' \ -d code_verifier=test-verifier-0123456789-0123456789-0123456789 \ --data-urlencode resource=https://mcp.example.com/mcpThe endpoint is set up correctly when it refuses the made-up code, and only the code:
HTTP/1.1 400 Bad RequestContent-Type: application/json
{"error":"invalid_grant"}If the problem is the resource parameter, you see this instead:
HTTP/1.1 400 Bad RequestContent-Type: application/json
{"error":"invalid_target","error_description":"resource indicators are not supported"}To test a real exchange, run the browser sign-in from
authorization, then send the code from the
redirect with the same verifier within the code’s lifetime. A 200 with "token_type": "Bearer"
means it works. Then select Sign in to scan again on the server’s page.
References
Section titled “References”- MCP authorization
2026-07-28: resource parameter implementation and access token usage - RFC 6749 §4.1.3 and §5.2: the token request and its errors
- RFC 8707 §2: resource indicators and
invalid_target - RFC 7636 §4.6: verifying the PKCE code verifier
- RFC 6750 §4: the Bearer token type
- OpenAI: Authentication, “Echo the resource parameter throughout the OAuth flow”
- Claude: Authentication for connectors, “Token endpoint requirements”