@@ -1371,6 +1371,75 @@ async def test_none_auth_method_public_client(
13711371 token_response = response .json ()
13721372 assert "access_token" in token_response
13731373
1374+ @pytest .mark .anyio
1375+ async def test_none_auth_method_ignores_stored_client_secret (
1376+ self , test_client : httpx .AsyncClient , mock_oauth_provider : MockOAuthProvider , pkce_challenge : dict [str , str ]
1377+ ):
1378+ """Test that 'none' auth method works even if client has a stored secret.
1379+
1380+ This tests the fix for issue #1842: when token_endpoint_auth_method="none",
1381+ the server should not require client_secret even if one is stored for the client.
1382+ This can happen if a client was previously registered with a secret but later
1383+ changed to public authentication.
1384+ """
1385+ # Register a public client with token_endpoint_auth_method="none"
1386+ client_metadata = {
1387+ "redirect_uris" : ["https://client.example.com/callback" ],
1388+ "client_name" : "Public Client With Stored Secret" ,
1389+ "token_endpoint_auth_method" : "none" ,
1390+ "grant_types" : ["authorization_code" , "refresh_token" ],
1391+ }
1392+
1393+ response = await test_client .post ("/register" , json = client_metadata )
1394+ assert response .status_code == 201
1395+ client_info = response .json ()
1396+ assert client_info ["token_endpoint_auth_method" ] == "none"
1397+
1398+ # Manually add a client_secret to the stored client (simulating edge case)
1399+ stored_client = await mock_oauth_provider .get_client (client_info ["client_id" ])
1400+ assert stored_client is not None
1401+ # Create a modified client with a secret
1402+ modified_client = OAuthClientInformationFull (
1403+ client_id = stored_client .client_id ,
1404+ client_id_issued_at = stored_client .client_id_issued_at ,
1405+ client_secret = "secret_that_should_be_ignored" ,
1406+ client_secret_expires_at = None ,
1407+ redirect_uris = stored_client .redirect_uris ,
1408+ token_endpoint_auth_method = "none" , # Still using 'none' auth method
1409+ grant_types = stored_client .grant_types ,
1410+ response_types = stored_client .response_types ,
1411+ client_name = stored_client .client_name ,
1412+ scope = stored_client .scope ,
1413+ )
1414+ mock_oauth_provider .clients [client_info ["client_id" ]] = modified_client
1415+
1416+ auth_code = f"code_{ int (time .time ())} "
1417+ mock_oauth_provider .auth_codes [auth_code ] = AuthorizationCode (
1418+ code = auth_code ,
1419+ client_id = client_info ["client_id" ],
1420+ code_challenge = pkce_challenge ["code_challenge" ],
1421+ redirect_uri = AnyUrl ("https://client.example.com/callback" ),
1422+ redirect_uri_provided_explicitly = True ,
1423+ scopes = ["read" , "write" ],
1424+ expires_at = time .time () + 600 ,
1425+ )
1426+
1427+ # Token request without any client secret should still succeed
1428+ # because token_endpoint_auth_method="none"
1429+ response = await test_client .post (
1430+ "/token" ,
1431+ data = {
1432+ "grant_type" : "authorization_code" ,
1433+ "client_id" : client_info ["client_id" ],
1434+ "code" : auth_code ,
1435+ "code_verifier" : pkce_challenge ["code_verifier" ],
1436+ "redirect_uri" : "https://client.example.com/callback" ,
1437+ },
1438+ )
1439+ assert response .status_code == 200
1440+ token_response = response .json ()
1441+ assert "access_token" in token_response
1442+
13741443
13751444class TestAuthorizeEndpointErrors :
13761445 """Test error handling in the OAuth authorization endpoint."""
0 commit comments