-
Notifications
You must be signed in to change notification settings - Fork 3k
feat: fully support Basic Authorization header at token request #1847
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
feat: fully support Basic Authorization header at token request #1847
Conversation
This test case only contains client_id and client_secret at Basic Authentication header, assuming that there is no client_id and client_secret at form data.
This data class contains creds plus auth method
Retrieve client_id from auth header or the body, then retrieve client via that client_id. After that, compare auth method.
`match` causes coverage error on python 3.10.
|
|
||
| return client | ||
|
|
||
| async def _get_credentials(self, request: Request) -> ClientCredentials: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: the core extraction logic of client_id and client_secret is as same as before.
|
|
||
| @pytest.mark.anyio | ||
| async def test_wrong_auth_method_without_valid_credentials_fails( | ||
| async def test_wrong_auth_method_fails( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: test fails as same as before, only exception message has been changed, due to the auth method mismatch.
| # RFC 6749: authentication failures return "invalid_client" | ||
| assert error_response["error"] == "invalid_client" | ||
| assert "Missing or invalid Basic authentication" in error_response["error_description"] | ||
| assert "Expected client_secret_basic authentication method" in error_response["error_description"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: test fails as same as before, only exception message has been changed, due to the auth method mismatch.
|
|
||
| @pytest.mark.anyio | ||
| async def test_basic_auth_client_id_mismatch_fails( | ||
| async def test_basic_auth_takes_precedence( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: the behaviour of the test has been changed. After the changes, basic auth takes precedence over form data, so even if there is a mismatch, the middleware retrieves the client_id from the header.
This behaviour is intended due to RFC 6749 Section 2.3.1:
Including the client credentials in the request-body using the two parameters is NOT RECOMMENDED and SHOULD be limited to clients unable to directly utilize the HTTP Basic authentication scheme (or other password-based HTTP authentication schemes).
If this is inappropriate, alternatives are:
- behave AS-IS (throw mismatch exception)
- disallow dup auth method
- throw exception which indicates that more than one authentication method was used.
Summary
This PR implements OAuth client authentication by supporting HTTP Basic authentication as defined in RFC 6749 Section 2.3.1., especially when
client_idis provided only from the header.The key changes are:
client_idoptional in token request body when using Basic auth (as the spec allows)Previously, the code would first read
client_idfrom the form body and then validate against the auth method. This caused issues when clients used Basic auth without includingclient_idin the request body, which is valid per the OAuth spec.Motivation and Context
client_secret_basicshould send credentials via the Authorization header, and theclient_idin the request body is optional in this case.client_idin the body even when using Basic auth, which was non-compliant with the spec.How Has This Been Tested?
Unit Test
client_idin request bodyclient_idclient_idin bodyE2E Test
npx @modelcontextprotocol/inspector+ OAuth provider mockup, and worked as expected.Screenshots
Breaking Changes
Types of changes
Checklist
I have added or updated documentation as neededno docs need to be updatedAdditional context