@@ -2365,30 +2365,31 @@ def test_authenticate_oidc_auto_renew_expired_access_token_initial_client_creden
23652365 requests_mock .get (API_URL , json = {"api_version" : "1.0.0" })
23662366 client_id = "myclient"
23672367 client_secret = "$3cr3t"
2368- issuer = "https://oidc.test"
2368+ oidc_issuer = "https://oidc.test"
23692369 requests_mock .get (
23702370 API_URL + "credentials/oidc" ,
2371- json = {"providers" : [{"id" : "oi" , "issuer" : issuer , "title" : "example" , "scopes" : ["openid" ]}]},
2371+ json = {"providers" : [{"id" : "oi" , "issuer" : oidc_issuer , "title" : "example" , "scopes" : ["openid" ]}]},
23722372 )
23732373 oidc_mock = OidcMock (
23742374 requests_mock = requests_mock ,
23752375 expected_grant_type = "client_credentials" ,
23762376 expected_client_id = client_id ,
23772377 expected_fields = {"client_secret" : client_secret , "scope" : "openid" },
2378- oidc_issuer = issuer ,
2378+ oidc_issuer = oidc_issuer ,
23792379 )
23802380
23812381 _setup_get_me_handler (
23822382 requests_mock = requests_mock , oidc_mock = oidc_mock , token_invalid_status_code = token_invalid_status_code
23832383 )
23842384 caplog .set_level (logging .INFO )
23852385
2386- # Explicit authentication with `authenticate_oidc_refresh_token `
2386+ # Initial authentication with `authenticate_oidc_client_credentials `
23872387 conn = Connection (API_URL , refresh_token_store = refresh_token_store )
23882388 assert isinstance (conn .auth , NullAuth )
23892389 conn .authenticate_oidc_client_credentials (client_id = client_id , client_secret = client_secret )
23902390 assert isinstance (conn .auth , BearerAuth )
23912391 assert conn .auth .bearer == "oidc/oi/" + oidc_mock .state ["access_token" ]
2392+
23922393 # Just one "client_credentials" auth request so far
23932394 assert [h ["grant_type" ] for h in oidc_mock .grant_request_history ] == ["client_credentials" ]
23942395 access_token1 = oidc_mock .state ["access_token" ]
@@ -2460,7 +2461,7 @@ def test_authenticate_oidc_auto_renew_expired_access_token_initial_client_creden
24602461 )
24612462 caplog .set_level (logging .INFO )
24622463
2463- # Explicit authentication with `authenticate_oidc_refresh_token `
2464+ # Initial authentication with `authenticate_oidc_client_credentials `
24642465 conn = Connection (API_URL , refresh_token_store = refresh_token_store )
24652466 assert isinstance (conn .auth , NullAuth )
24662467 conn .authenticate_oidc_client_credentials (client_id = client_id , client_secret = client_secret )
@@ -2477,9 +2478,9 @@ def test_authenticate_oidc_auto_renew_expired_access_token_initial_client_creden
24772478 "_used_access_token" : access_token1 ,
24782479 }
24792480
2480- # Expire access token don't accept client credentials anymore
2481+ # Expire access token, and don't accept client credentials anymore
24812482 oidc_mock .invalidate_access_token ()
2482- requests_mock . post ( oidc_mock .token_endpoint , status_code = 401 , text = "nope" )
2483+ oidc_mock .token_callback_client_credentials = oidc_mock . token_callback_block_400
24832484 # Do request that requires auth headers and might trigger re-authentication
24842485 assert f"{ token_invalid_status_code } TokenInvalid" not in caplog .text
24852486 with pytest .raises (
@@ -2508,16 +2509,7 @@ def test_try_access_token_refresh_initial_refresh_token(
25082509 oidc_issuer = "https://oidc.test"
25092510 requests_mock .get (
25102511 API_URL + "credentials/oidc" ,
2511- json = {
2512- "providers" : [
2513- {
2514- "id" : "oi" ,
2515- "issuer" : oidc_issuer ,
2516- "title" : "example" ,
2517- "scopes" : ["openid" ],
2518- }
2519- ]
2520- },
2512+ json = {"providers" : [{"id" : "oi" , "issuer" : oidc_issuer , "title" : "example" , "scopes" : ["openid" ]}]},
25212513 )
25222514 oidc_mock = OidcMock (
25232515 requests_mock = requests_mock ,
@@ -2593,16 +2585,7 @@ def test_try_access_token_refresh_initial_device_code(
25932585 oidc_issuer = "https://oidc.test"
25942586 requests_mock .get (
25952587 API_URL + "credentials/oidc" ,
2596- json = {
2597- "providers" : [
2598- {
2599- "id" : "oi" ,
2600- "issuer" : oidc_issuer ,
2601- "title" : "example" ,
2602- "scopes" : ["openid" ],
2603- }
2604- ]
2605- },
2588+ json = {"providers" : [{"id" : "oi" , "issuer" : oidc_issuer , "title" : "example" , "scopes" : ["openid" ]}]},
26062589 )
26072590 oidc_mock = OidcMock (
26082591 requests_mock = requests_mock ,
@@ -2680,6 +2663,88 @@ def test_try_access_token_refresh_initial_device_code(
26802663 }
26812664
26822665
2666+ @pytest .mark .parametrize (
2667+ ["block_token_endpoint" , "expect_refresh" ],
2668+ [
2669+ (False , True ),
2670+ (True , False ),
2671+ ],
2672+ )
2673+ def test_try_access_token_refresh_initial_client_credentials (
2674+ requests_mock , refresh_token_store , caplog , block_token_endpoint , expect_refresh , oidc_device_code_flow_checker
2675+ ):
2676+ requests_mock .get (API_URL , json = {"api_version" : "1.0.0" })
2677+ client_id = "myclient"
2678+ client_secret = "$3cr3t"
2679+ oidc_issuer = "https://oidc.test"
2680+ requests_mock .get (
2681+ API_URL + "credentials/oidc" ,
2682+ json = {"providers" : [{"id" : "oi" , "issuer" : oidc_issuer , "title" : "example" , "scopes" : ["openid" ]}]},
2683+ )
2684+ oidc_mock = OidcMock (
2685+ requests_mock = requests_mock ,
2686+ expected_grant_type = "client_credentials" ,
2687+ expected_client_id = client_id ,
2688+ expected_fields = {"client_secret" : client_secret , "scope" : "openid" },
2689+ oidc_issuer = oidc_issuer ,
2690+ )
2691+ _setup_get_me_handler (requests_mock = requests_mock , oidc_mock = oidc_mock )
2692+ caplog .set_level (logging .WARNING )
2693+
2694+ # Initial authentication with `authenticate_oidc_client_credentials`
2695+ conn = Connection (API_URL , refresh_token_store = refresh_token_store )
2696+ assert isinstance (conn .auth , NullAuth )
2697+ conn .authenticate_oidc_client_credentials (client_id = client_id , client_secret = client_secret )
2698+ assert isinstance (conn .auth , BearerAuth ) and conn .auth .bearer == "oidc/oi/" + oidc_mock .state ["access_token" ]
2699+
2700+ # Just one "refresh_token" auth request so far
2701+ assert [h ["grant_type" ] for h in oidc_mock .grant_request_history ] == [
2702+ "client_credentials" ,
2703+ ]
2704+ access_token1 = oidc_mock .state ["access_token" ]
2705+
2706+ # Do request that requires auth headers
2707+ assert conn .describe_account () == {
2708+ "user_id" : "john" ,
2709+ "_used_oidc_provider" : "oi" ,
2710+ "_used_access_token" : access_token1 ,
2711+ }
2712+
2713+ # Prepare for refresh attempt
2714+ if block_token_endpoint :
2715+ oidc_mock .token_callback_client_credentials = oidc_mock .token_callback_block_400
2716+
2717+ # Trigger refresh
2718+ refreshed = conn .try_access_token_refresh (reason = "Can I Haz Fresh" )
2719+
2720+ # Two "refresh_token" auth request attempts should have happened now
2721+ assert [h ["grant_type" ] for h in oidc_mock .grant_request_history ] == [
2722+ "client_credentials" ,
2723+ "client_credentials" ,
2724+ ]
2725+ access_token2 = oidc_mock .state ["access_token" ]
2726+
2727+ if expect_refresh :
2728+ assert refreshed == True
2729+ assert access_token2 != access_token1
2730+ assert caplog .messages == []
2731+ else :
2732+ assert refreshed == False
2733+ assert access_token2 == access_token1
2734+ assert caplog .messages == [
2735+ dirty_equals .IsStr (
2736+ regex = "Failed to obtain new access token.*grant 'client_credentials'.*block_400.*Reason: Can I Haz Fresh"
2737+ )
2738+ ]
2739+
2740+ # New request with fresh token?
2741+ assert conn .describe_account () == {
2742+ "user_id" : "john" ,
2743+ "_used_oidc_provider" : "oi" ,
2744+ "_used_access_token" : access_token2 ,
2745+ }
2746+
2747+
26832748class TestAuthenticateOidcAccessToken :
26842749 @pytest .fixture (autouse = True )
26852750 def _setup (self , requests_mock ):
0 commit comments