Skip to content

Commit 53167d2

Browse files
committed
Refactoring authcode.py
1 parent dca1bfb commit 53167d2

File tree

3 files changed

+55
-27
lines changed

3 files changed

+55
-27
lines changed

msal/application.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -847,21 +847,24 @@ def acquire_token_interactive(self,
847847
):
848848
if not port:
849849
port = _get_open_port()
850+
redirect_uri = "http://localhost:%d" % port
850851
request_state = str(uuid.uuid4())
851852
auth_url = self.get_authorization_request_url(
852853
scopes=scopes,
853854
login_hint=login_hint,
854-
redirect_uri="http://localhost:%d" % port,
855+
redirect_uri=redirect_uri,
855856
response_type="code",
856857
state=request_state,
857858
prompt=prompt,
858859
domain_hint=domain_hint,
859860
claims_challenge=claims_challenge,)
860-
auth_code, received_state = obtain_auth_code(port, auth_uri=auth_url)
861-
if request_state != received_state:
862-
raise ValueError("State does not match")
861+
auth_code, state = obtain_auth_code(port, auth_uri=auth_url)
862+
if not auth_code:
863+
raise TimeoutError("Server timed out")
864+
if request_state != state:
865+
return ValueError("State does not match")
863866
return self.acquire_token_by_authorization_code(
864-
auth_code, scopes, redirect_uri="http://localhost:%d" %port,
867+
auth_code, scopes, redirect_uri=redirect_uri,
865868
claims_challenge=claims_challenge)
866869

867870

msal/oauth2cli/authcode.py

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -36,25 +36,11 @@ def obtain_auth_code(listen_port, auth_uri=None):
3636
:param auth_uri: If provided, this function will try to open a local browser.
3737
:return: Hang indefinitely, until it receives and then return the auth code.
3838
"""
39-
exit_hint = "Visit http://localhost:{p}?code=exit to abort".format(p=listen_port)
40-
logger.warning(exit_hint)
4139
if auth_uri:
42-
page = "http://localhost:{p}?{q}".format(p=listen_port, q=urlencode({
43-
"text": "Open this link to sign in. You may use incognito window",
44-
"link": auth_uri,
45-
"exit_hint": exit_hint,
46-
}))
4740
browse(auth_uri)
48-
server = HTTPServer(("", int(listen_port)), AuthCodeReceiver)
49-
try:
50-
server.authcode = None
51-
while not server.authcode:
52-
# Derived from
53-
# https://docs.python.org/2/library/basehttpserver.html#more-examples
54-
server.handle_request()
55-
return server.authcode, server.state
56-
finally:
57-
server.server_close()
41+
server = AuthcodeRedirectServer(int(listen_port))
42+
return server.get_auth_code()
43+
5844

5945
def browse(auth_uri):
6046
controller = webbrowser.get() # Get a default controller
@@ -69,15 +55,16 @@ def browse(auth_uri):
6955
logger.info("Please open a browser on THIS device to visit: %s" % auth_uri)
7056
controller.open(auth_uri)
7157

58+
7259
class AuthCodeReceiver(BaseHTTPRequestHandler):
7360
def do_GET(self):
7461
# For flexibility, we choose to not check self.path matching redirect_uri
7562
#assert self.path.startswith('/THE_PATH_REGISTERED_BY_THE_APP')
7663
qs = parse_qs(urlparse(self.path).query)
7764
if qs.get('code'): # Then store it into the server instance
78-
ac = self.server.authcode = qs['code'][0]
79-
self.server.state = qs['state'][0]
80-
self._send_full_response('Authcode:\n{}'.format(ac)) #This is where the exit message will go
65+
self.server.auth_code = qs['code'][0]
66+
self.server.state = qs.get('state', [None])[0]
67+
self._send_full_response('Authentication complete. You can close this window')
8168
# NOTE: Don't do self.server.shutdown() here. It'll halt the server.
8269
elif qs.get('text') and qs.get('link'): # Then display a landing page
8370
self._send_full_response(
@@ -96,6 +83,32 @@ def _send_full_response(self, body, is_ok=True):
9683
self.wfile.write(body.encode("utf-8"))
9784

9885

86+
class AuthcodeRedirectServer(HTTPServer):
87+
88+
def __init__(self, port):
89+
HTTPServer.__init__(self, ("", port), AuthCodeReceiver)
90+
self.state = None
91+
self.auth_code = None
92+
self.timeout = 300
93+
94+
def get_auth_code(self):
95+
try:
96+
while not self.auth_code:
97+
try:
98+
# Derived from
99+
# https://docs.python.org/2/library/basehttpserver.html#more-examples
100+
self.handle_request()
101+
except ValueError:
102+
break
103+
finally:
104+
self.server_close()
105+
106+
return self.auth_code, self.state
107+
108+
def handle_timeout(self):
109+
"""Break the request-handling loop by tearing down the server"""
110+
self.server_close()
111+
99112
if __name__ == '__main__':
100113
logging.basicConfig(level=logging.INFO)
101114
p = parser = argparse.ArgumentParser(

tests/test_e2e.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@
1212
logger = logging.getLogger(__name__)
1313
logging.basicConfig(level=logging.INFO)
1414

15+
try: # Python 3
16+
from urllib.parse import urlparse, parse_qs, urlencode
17+
except ImportError: # Fall back to Python 2
18+
from urllib import urlencode
19+
20+
1521

1622
def _get_app_and_auth_code(
1723
client_id,
@@ -24,8 +30,14 @@ def _get_app_and_auth_code(
2430
app = msal.ClientApplication(
2531
client_id, client_secret, authority=authority, http_client=MinimalHttpClient())
2632
redirect_uri = "http://localhost:%d" % port
27-
ac = obtain_auth_code(port, auth_uri=app.get_authorization_request_url(
28-
scopes, redirect_uri=redirect_uri, **kwargs))
33+
exit_hint = "Visit http://localhost:{p}?code=exit to abort".format(p=port)
34+
ac = obtain_auth_code(port, auth_uri="http://localhost:{p}?{q}".format(p=port, q=urlencode({
35+
"text": "Open this link to sign in. You may use incognito window",
36+
"link": app.get_authorization_request_url(
37+
scopes, redirect_uri=redirect_uri, **kwargs),
38+
"exit_hint": exit_hint,
39+
})))
40+
logger.warning(exit_hint)
2941
assert ac is not None
3042
return (app, ac, redirect_uri)
3143

0 commit comments

Comments
 (0)