@@ -291,6 +291,79 @@ async def test_token_error_handling_refresh_token(
291291 assert data ["error_description" ] == "The requested scope is invalid"
292292
293293
294+ @pytest .mark .anyio
295+ async def test_token_unsupported_grant_type_returns_unsupported_grant_type (
296+ client : httpx2 .AsyncClient ,
297+ registered_client : dict [str , Any ],
298+ ):
299+ """RFC 6749 §5.2: Server must return unsupported_grant_type when the grant type is not supported."""
300+ client_id = registered_client ["client_id" ]
301+ client_secret = registered_client ["client_secret" ]
302+
303+ response = await client .post (
304+ "/token" ,
305+ data = {
306+ "grant_type" : "client_credentials" ,
307+ "client_id" : client_id ,
308+ "client_secret" : client_secret ,
309+ },
310+ )
311+ assert response .status_code == 400
312+ data = response .json ()
313+ assert data ["error" ] == "unsupported_grant_type"
314+ assert "client_credentials" in data ["error_description" ]
315+
316+
317+ @pytest .mark .anyio
318+ async def test_token_unauthorized_client_grant_type_returns_unauthorized_client (
319+ client : httpx2 .AsyncClient ,
320+ registered_client : dict [str , Any ],
321+ oauth_provider : MockOAuthProvider ,
322+ ):
323+ """RFC 6749 §5.2: Server must return unauthorized_client when the client is not authorized for grant type."""
324+ client_id = registered_client ["client_id" ]
325+ client_secret = registered_client ["client_secret" ]
326+
327+ # Modify the registered client to only allow 'authorization_code'
328+ stored_client = oauth_provider .clients [client_id ]
329+ oauth_provider .clients [client_id ] = stored_client .model_copy (update = {"grant_types" : ["authorization_code" ]})
330+
331+ response = await client .post (
332+ "/token" ,
333+ data = {
334+ "grant_type" : "refresh_token" ,
335+ "refresh_token" : "dummy_refresh_token" ,
336+ "client_id" : client_id ,
337+ "client_secret" : client_secret ,
338+ },
339+ )
340+ assert response .status_code == 400
341+ data = response .json ()
342+ assert data ["error" ] == "unauthorized_client"
343+
344+
345+ @pytest .mark .anyio
346+ async def test_revoke_invalid_client_authentication_returns_401 (
347+ client : httpx2 .AsyncClient ,
348+ registered_client : dict [str , Any ],
349+ ):
350+ """RFC 7009 §2.2.1 & RFC 6749 §5.2: Revocation endpoint returns 401 invalid_client on auth failure."""
351+ client_id = registered_client ["client_id" ]
352+
353+ # Wrong client_secret
354+ response = await client .post (
355+ "/revoke" ,
356+ data = {
357+ "token" : "some_token" ,
358+ "client_id" : client_id ,
359+ "client_secret" : "wrong_secret" ,
360+ },
361+ )
362+ assert response .status_code == 401
363+ data = response .json ()
364+ assert data ["error" ] == "invalid_client"
365+
366+
294367_FORM = "application/x-www-form-urlencoded"
295368
296369
0 commit comments