23
23
24
24
logger = logging .getLogger (__name__ )
25
25
26
- def obtain_auth_code (listen_port , auth_uri = None ):
26
+ def obtain_auth_code (listen_port , auth_uri = None , text = None , request_state = None ):
27
27
"""This function will start a web server listening on http://localhost:port
28
28
and then you need to open a browser on this device and visit your auth_uri.
29
29
When interaction finishes, this function will return the auth code,
@@ -36,9 +36,18 @@ def obtain_auth_code(listen_port, auth_uri=None):
36
36
:param auth_uri: If provided, this function will try to open a local browser.
37
37
:return: Hang indefinitely, until it receives and then return the auth code.
38
38
"""
39
- if auth_uri :
39
+ if text :
40
+ exit_hint = "Visit http://localhost:{p}?auth_code=exit to abort" .format (p = listen_port )
41
+ browse ("http://localhost:{p}?{q}" .format (
42
+ p = listen_port , q = urlencode ({
43
+ "text" : text ,
44
+ "link" : auth_uri ,
45
+ "exit_hint" : exit_hint ,
46
+ })))
47
+ logger .warning (exit_hint )
48
+ else :
40
49
browse (auth_uri )
41
- server = AuthcodeRedirectServer (int (listen_port ))
50
+ server = AuthcodeRedirectServer (int (listen_port ), request_state )
42
51
return server .get_auth_code ()
43
52
44
53
@@ -62,8 +71,9 @@ def do_GET(self):
62
71
#assert self.path.startswith('/THE_PATH_REGISTERED_BY_THE_APP')
63
72
qs = parse_qs (urlparse (self .path ).query )
64
73
if qs .get ('code' ): # Then store it into the server instance
74
+ if self .server .state and self .server .state != qs .get ('state' , [None ])[0 ]:
75
+ raise ValueError ("State does not match" )
65
76
self .server .auth_code = qs ['code' ][0 ]
66
- self .server .state = qs .get ('state' , [None ])[0 ]
67
77
self ._send_full_response ('Authentication complete. You can close this window' )
68
78
# NOTE: Don't do self.server.shutdown() here. It'll halt the server.
69
79
elif qs .get ('text' ) and qs .get ('link' ): # Then display a landing page
@@ -85,9 +95,9 @@ def _send_full_response(self, body, is_ok=True):
85
95
86
96
class AuthcodeRedirectServer (HTTPServer ):
87
97
88
- def __init__ (self , port ):
98
+ def __init__ (self , port , request_state ):
89
99
HTTPServer .__init__ (self , ("" , port ), AuthCodeReceiver )
90
- self .state = None
100
+ self .state = request_state
91
101
self .auth_code = None
92
102
self .timeout = 300
93
103
@@ -100,10 +110,12 @@ def get_auth_code(self):
100
110
self .handle_request ()
101
111
except ValueError :
102
112
break
113
+ except IOError : # Python 2 throws an IOError handle timeout closes server
114
+ break
103
115
finally :
104
116
self .server_close ()
105
117
106
- return self .auth_code , self . state
118
+ return self .auth_code
107
119
108
120
def handle_timeout (self ):
109
121
"""Break the request-handling loop by tearing down the server"""
0 commit comments