1- """Tests for form_post response mode in authorization code flow"""
1+ """Integration tests for form_post response mode in authorization code flow"""
22import unittest
33import warnings
44try :
77 from urlparse import urlparse , parse_qs
88
99from msal .oauth2cli import Client
10+ from msal .oauth2cli .authcode import AuthCodeReceiver
11+ import requests
1012
1113
12- class TestResponseMode (unittest .TestCase ):
13- """Test response_mode parameter in authorization code flow"""
14+ class TestResponseModeIntegration (unittest .TestCase ):
15+ """Integration test for response_mode with end-to-end authentication flow"""
1416
1517 def setUp (self ):
1618 self .client = Client (
@@ -20,92 +22,103 @@ def setUp(self):
2022 },
2123 "test_client_id"
2224 )
23-
24- def test_default_response_mode_is_form_post (self ):
25- """Test that response_mode defaults to form_post for security"""
26- flow = self .client .initiate_auth_code_flow (
27- scope = ["openid" , "profile" ],
28- redirect_uri = "http://localhost:8080"
29- )
30-
31- # Parse the auth_uri to check query parameters
32- parsed = urlparse (flow ["auth_uri" ])
33- params = parse_qs (parsed .query )
25+
26+ def _send_post_auth_response (self , port , ** params ):
27+ """Helper to send POST auth response to the receiver"""
28+ try :
29+ from urllib .parse import urlencode
30+ except ImportError :
31+ from urllib import urlencode
3432
35- # Verify response_mode is set to form_post
36- self .assertIn ("response_mode" , params )
37- self .assertEqual (params ["response_mode" ][0 ], "form_post" )
33+ response = requests .post (
34+ "http://localhost:{}" .format (port ),
35+ data = urlencode (params ),
36+ headers = {"Content-Type" : "application/x-www-form-urlencoded" }
37+ )
38+ return response
3839
39- def test_explicit_query_mode_shows_security_warning (self ):
40- """Test that attempting to use query mode raises a security warning"""
40+ def test_query_mode_override_with_successful_post_authentication (self ):
41+ """
42+ Comprehensive integration test: When response_mode='query' is set,
43+ verify deprecation warning, override to form_post, and successful POST authentication.
44+ """
45+ # Part 1: Verify deprecation and override warnings
4146 with warnings .catch_warnings (record = True ) as w :
4247 warnings .simplefilter ("always" )
4348 flow = self .client .initiate_auth_code_flow (
4449 scope = ["openid" , "profile" ],
4550 redirect_uri = "http://localhost:8080" ,
46- response_mode = "query"
51+ response_mode = "query" # Explicitly request query mode (should be overridden)
4752 )
4853
49- # Verify a warning was raised
50- self .assertEqual (len (w ), 1 )
51- self .assertTrue (issubclass (w [0 ].category , Warning ))
52- self .assertIn ("security" , str (w [0 ].message ).lower ())
53-
54- # Verify form_post is still enforced despite explicit query request
55- parsed = urlparse (flow ["auth_uri" ])
56- params = parse_qs (parsed .query )
57- self .assertEqual (params ["response_mode" ][0 ], "form_post" )
58-
59- def test_explicit_fragment_mode_shows_security_warning (self ):
60- """Test that attempting to use fragment mode raises a security warning"""
61- with warnings .catch_warnings (record = True ) as w :
62- warnings .simplefilter ("always" )
63- flow = self .client .initiate_auth_code_flow (
64- scope = ["openid" , "profile" ],
65- redirect_uri = "http://localhost:8080" ,
66- response_mode = "fragment"
67- )
54+ # Verify deprecation warning
55+ deprecation_warnings = [x for x in w if issubclass (x .category , DeprecationWarning )]
56+ self .assertEqual (len (deprecation_warnings ), 1 , "Should have exactly one deprecation warning" )
57+ self .assertIn ("deprecated" , str (deprecation_warnings [0 ].message ).lower ())
6858
69- # Verify a warning was raised
70- self .assertEqual (len (w ), 1 )
71- self .assertIn ("fragment" , str (w [0 ].message ))
59+ # Verify override warning
60+ user_warnings = [x for x in w if issubclass (x .category , UserWarning )]
61+ self .assertEqual (len (user_warnings ), 1 , "Should have exactly one override warning" )
62+ self .assertIn ("overridden" , str (user_warnings [0 ].message ).lower ())
7263
73- # Verify form_post is still enforced
64+ # Part 2: Verify form_post is enforced in auth_uri
7465 parsed = urlparse (flow ["auth_uri" ])
7566 params = parse_qs (parsed .query )
76- self .assertEqual (params ["response_mode" ][0 ], "form_post" )
77-
78- def test_build_auth_request_params_enforces_form_post (self ):
79- """Test that _build_auth_request_params enforces form_post"""
80- params = self .client ._build_auth_request_params (
81- response_type = "code" ,
82- redirect_uri = "http://localhost:8080" ,
83- scope = ["openid" , "profile" ],
84- state = "test_state"
85- )
86-
87- # Verify response_mode is form_post
8867 self .assertIn ("response_mode" , params )
89- self .assertEqual (params ["response_mode" ], "form_post" )
68+ self .assertEqual (params ["response_mode" ][0 ], "form_post" ,
69+ "response_mode should be overridden to form_post" )
70+
71+ # Part 3: Verify actual POST authentication works (form_post flow)
72+ with AuthCodeReceiver () as receiver :
73+ test_code = "test_auth_code_xyz"
74+ test_state = "test_state_abc"
75+
76+ receiver ._scheduled_actions = [(
77+ 1 ,
78+ lambda : self ._send_post_auth_response (
79+ receiver .get_port (),
80+ code = test_code ,
81+ state = test_state
82+ )
83+ )]
84+
85+ result = receiver .get_auth_response (
86+ timeout = 3 ,
87+ state = test_state ,
88+ success_template = "Success: Got code $code" ,
89+ )
90+
91+ # Verify the POST request succeeded
92+ self .assertIsNotNone (result , "POST authentication should succeed" )
93+ self .assertEqual (result .get ("code" ), test_code ,
94+ "Should receive auth code via POST (form_post)" )
95+ self .assertEqual (result .get ("state" ), test_state ,
96+ "Should receive correct state via POST" )
9097
91- def test_build_auth_request_params_ignores_explicit_query_mode (self ):
92- """Test that _build_auth_request_params ignores explicit query mode"""
93- with warnings .catch_warnings (record = True ) as w :
94- warnings .simplefilter ("always" )
95- params = self .client ._build_auth_request_params (
96- response_type = "code" ,
97- redirect_uri = "http://localhost:8080" ,
98- scope = ["openid" , "profile" ],
99- state = "test_state" ,
100- response_mode = "query"
98+ def test_query_mode_get_request_rejected (self ):
99+ """
100+ Verify that GET requests with auth code are rejected when form_post is enforced.
101+ """
102+ with AuthCodeReceiver () as receiver :
103+ test_code = "test_auth_code_via_get"
104+
105+ # Try to send auth code via GET (query string)
106+ try :
107+ from urllib .parse import urlencode
108+ except ImportError :
109+ from urllib import urlencode
110+
111+ response = requests .get (
112+ "http://localhost:{}?{}" .format (
113+ receiver .get_port (),
114+ urlencode ({"code" : test_code , "state" : "test" })
115+ )
101116 )
102117
103- # Verify warning was raised
104- self .assertGreater (len (w ), 0 )
105-
106- # Verify form_post is enforced despite explicit request
107- self .assertIn ("response_mode" , params )
108- self .assertEqual (params ["response_mode" ], "form_post" )
118+ # Verify the GET request is rejected
119+ self .assertEqual (response .status_code , 400 , "GET with auth code should be rejected" )
120+ self .assertIn ("not supported" , response .text .lower (),
121+ "Error message should indicate method not supported" )
109122
110123
111124if __name__ == '__main__' :
0 commit comments