@@ -277,9 +277,7 @@ def test_auth_webbrowser_fail_webbrowser(
277277 )
278278 captured = capsys .readouterr ()
279279 assert captured .out == (
280- "Initiating login request with your identity provider. A browser window "
281- "should have opened for you to complete the login. If you can't see it, "
282- "check existing browser windows, or your OS settings. Press CTRL+C to "
280+ "Initiating login request with your identity provider. Press CTRL+C to "
283281 f"abort and try again...\n Going to open: { REF_SSO_URL if disable_console_login else REF_CONSOLE_LOGIN_SSO_URL } to authenticate...\n We were unable to open a browser window for "
284282 "you, please open the url above manually then paste the URL you "
285283 "are redirected to into the terminal.\n "
@@ -336,10 +334,10 @@ def test_auth_webbrowser_fail_webserver(_, capsys, disable_console_login):
336334 )
337335 captured = capsys .readouterr ()
338336 assert captured .out == (
339- "Initiating login request with your identity provider. A browser window "
337+ "Initiating login request with your identity provider. Press CTRL+C to "
338+ f"abort and try again...\n Going to open: { REF_SSO_URL if disable_console_login else REF_CONSOLE_LOGIN_SSO_URL } to authenticate...\n A browser window "
340339 "should have opened for you to complete the login. If you can't see it, "
341- "check existing browser windows, or your OS settings. Press CTRL+C to "
342- f"abort and try again...\n Going to open: { REF_SSO_URL if disable_console_login else REF_CONSOLE_LOGIN_SSO_URL } to authenticate...\n "
340+ "check existing browser windows, or your OS settings.\n "
343341 )
344342 assert rest ._connection .errorhandler .called # an error
345343 assert auth .assertion_content is None
@@ -751,6 +749,78 @@ def test_auth_webbrowser_socket_reuseport_option_not_set_with_no_flag(monkeypatc
751749 assert auth .assertion_content == ref_token
752750
753751
752+ @pytest .mark .parametrize ("force_auth_server" , [True , False ])
753+ @patch ("secrets.token_bytes" , return_value = PROOF_KEY )
754+ def test_auth_webbrowser_force_auth_server (_ , monkeypatch , force_auth_server ):
755+ """Authentication by WebBrowser with SNOWFLAKE_AUTH_FORCE_SERVER environment variable."""
756+ ref_token = "MOCK_TOKEN"
757+ rest = _init_rest (REF_SSO_URL , REF_PROOF_KEY , disable_console_login = True )
758+
759+ # Set environment variable
760+ if force_auth_server :
761+ monkeypatch .setenv ("SNOWFLAKE_AUTH_FORCE_SERVER" , "true" )
762+ else :
763+ monkeypatch .delenv ("SNOWFLAKE_AUTH_FORCE_SERVER" , raising = False )
764+
765+ # mock socket
766+ mock_socket_pkg = _init_socket (
767+ recv_side_effect_func = recv_setup ([successful_web_callback (ref_token )])
768+ )
769+
770+ # mock webbrowser - simulate browser failing to open
771+ mock_webbrowser = MagicMock ()
772+ mock_webbrowser .open_new .return_value = False
773+
774+ # Mock select.select to return socket client
775+ with mock .patch (
776+ "select.select" , return_value = ([mock_socket_pkg .return_value ], [], [])
777+ ):
778+ auth = AuthByWebBrowser (
779+ application = APPLICATION ,
780+ webbrowser_pkg = mock_webbrowser ,
781+ socket_pkg = mock_socket_pkg ,
782+ )
783+
784+ if force_auth_server :
785+ # When SNOWFLAKE_AUTH_FORCE_SERVER is true, should continue with server flow even if browser fails
786+ auth .prepare (
787+ conn = rest ._connection ,
788+ authenticator = AUTHENTICATOR ,
789+ service_name = SERVICE_NAME ,
790+ account = ACCOUNT ,
791+ user = USER ,
792+ password = PASSWORD ,
793+ )
794+ assert not rest ._connection .errorhandler .called # no error
795+ assert auth .assertion_content == ref_token
796+ body = {"data" : {}}
797+ auth .update_body (body )
798+ assert body ["data" ]["TOKEN" ] == ref_token
799+ assert body ["data" ]["AUTHENTICATOR" ] == EXTERNAL_BROWSER_AUTHENTICATOR
800+ assert body ["data" ]["PROOF_KEY" ] == REF_PROOF_KEY
801+ else :
802+ # When SNOWFLAKE_AUTH_FORCE_SERVER is false/unset, should fall back to manual URL input
803+ with patch (
804+ "builtins.input" ,
805+ return_value = f"http://example.com/sso?token={ ref_token } " ,
806+ ):
807+ auth .prepare (
808+ conn = rest ._connection ,
809+ authenticator = AUTHENTICATOR ,
810+ service_name = SERVICE_NAME ,
811+ account = ACCOUNT ,
812+ user = USER ,
813+ password = PASSWORD ,
814+ )
815+ assert not rest ._connection .errorhandler .called # no error
816+ assert auth .assertion_content == ref_token
817+ body = {"data" : {}}
818+ auth .update_body (body )
819+ assert body ["data" ]["TOKEN" ] == ref_token
820+ assert body ["data" ]["AUTHENTICATOR" ] == EXTERNAL_BROWSER_AUTHENTICATOR
821+ assert body ["data" ]["PROOF_KEY" ] == REF_PROOF_KEY
822+
823+
754824@pytest .mark .parametrize ("authenticator" , ["EXTERNALBROWSER" , "externalbrowser" ])
755825def test_externalbrowser_authenticator_is_case_insensitive (monkeypatch , authenticator ):
756826 """Test that external browser authenticator is case insensitive."""
0 commit comments