@@ -33,19 +33,9 @@ def obtain_auth_code(listen_port, auth_uri=None): # Historically only used in t
3333 ).get ("code" )
3434
3535
36- def _browse (auth_uri ):
36+ def _browse (auth_uri ): # throws ImportError, possibly webbrowser.Error in future
3737 import webbrowser # Lazy import. Some distro may not have this.
38- controller = webbrowser .get () # Get a default controller
39- # Some Linux Distro does not setup default browser properly,
40- # so we try to explicitly use some popular browser, if we found any.
41- for browser in ["chrome" , "firefox" , "safari" , "windows-default" ]:
42- try :
43- controller = webbrowser .get (browser )
44- break
45- except webbrowser .Error :
46- pass # This browser is not installed. Try next one.
47- logger .info ("Please open a browser on THIS device to visit: %s" % auth_uri )
48- controller .open (auth_uri )
38+ return webbrowser .open (auth_uri ) # Use default browser. Customizable by $BROWSER
4939
5040
5141def _qs2kv (qs ):
@@ -130,14 +120,16 @@ def get_port(self):
130120 return self ._server .server_address [1 ]
131121
132122 def get_auth_response (self , auth_uri = None , timeout = None , state = None ,
133- welcome_template = None , success_template = None , error_template = None ):
134- """Wait and return the auth response, or None when timeout.
123+ welcome_template = None , success_template = None , error_template = None ,
124+ auth_uri_callback = None ,
125+ ):
126+ """Wait and return the auth response. Raise RuntimeError when timeout.
135127
136128 :param str auth_uri:
137129 If provided, this function will try to open a local browser.
138130 :param int timeout: In seconds. None means wait indefinitely.
139131 :param str state:
140- You may provide the state you used in auth_url ,
132+ You may provide the state you used in auth_uri ,
141133 then we will use it to validate incoming response.
142134 :param str welcome_template:
143135 If provided, your end user will see it instead of the auth_uri.
@@ -152,6 +144,10 @@ def get_auth_response(self, auth_uri=None, timeout=None, state=None,
152144 The page will be displayed when authentication encountered error.
153145 Placeholders can be any of these:
154146 https://tools.ietf.org/html/rfc6749#section-5.2
147+ :param callable auth_uri_callback:
148+ A function with the shape of lambda auth_uri: ...
149+ When a browser was unable to be launch, this function will be called,
150+ so that the app could tell user to manually visit the auth_uri.
155151 :return:
156152 The auth response of the first leg of Auth Code flow,
157153 typically {"code": "...", "state": "..."} or {"error": "...", ...}
@@ -164,8 +160,31 @@ def get_auth_response(self, auth_uri=None, timeout=None, state=None,
164160 logger .debug ("Abort by visit %s" , abort_uri )
165161 self ._server .welcome_page = Template (welcome_template or "" ).safe_substitute (
166162 auth_uri = auth_uri , abort_uri = abort_uri )
167- if auth_uri :
168- _browse (welcome_uri if welcome_template else auth_uri )
163+ if auth_uri : # Now attempt to open a local browser to visit it
164+ _uri = welcome_uri if welcome_template else auth_uri
165+ logger .info ("Open a browser on this device to visit: %s" % _uri )
166+ browser_opened = False
167+ try :
168+ browser_opened = _browse (_uri )
169+ except : # Had to use broad except, because the potential
170+ # webbrowser.Error is purposely undefined outside of _browse().
171+ # Absorb and proceed. Because browser could be manually run elsewhere.
172+ logger .exception ("_browse(...) unsuccessful" )
173+ if not browser_opened :
174+ if not auth_uri_callback :
175+ logger .warning (
176+ "Found no browser in current environment. "
177+ "If this program is being run inside a container "
178+ "which has access to host network "
179+ "(i.e. started by `docker run --net=host -it ...`), "
180+ "you can use browser on host to visit the following link. "
181+ "Otherwise, this auth attempt would either timeout "
182+ "(current timeout setting is {timeout}) "
183+ "or be aborted by CTRL+C. Auth URI: {auth_uri}" .format (
184+ auth_uri = _uri , timeout = timeout ))
185+ else : # Then it is the auth_uri_callback()'s job to inform the user
186+ auth_uri_callback (_uri )
187+
169188 self ._server .success_template = Template (success_template or
170189 "Authentication completed. You can close this window now." )
171190 self ._server .error_template = Template (error_template or
0 commit comments