Skip to content
This repository was archived by the owner on Oct 2, 2024. It is now read-only.

Commit 7b6ed59

Browse files
committed
Access token helper
Also remove dual-purpose authenticate() method because it would require a web browser dependency.
1 parent 9b7a105 commit 7b6ed59

File tree

2 files changed

+136
-29
lines changed

2 files changed

+136
-29
lines changed

src/onedrivesdk/auth_provider.py

Lines changed: 12 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -171,53 +171,36 @@ def get_auth_url(self, redirect_uri):
171171

172172
return "{}?{}".format(self._auth_server_url, urlencode(params))
173173

174-
def authenticate(self, redirect_uri, code=None, client_secret=None, resource=None):
175-
"""Takes in a gets the access token and creates a session.
174+
def authenticate(self, code, redirect_uri, client_secret, resource=None):
175+
"""Takes in a code, gets the access token and creates a session.
176176
177177
Args:
178178
code (str):
179179
The code provided by the oauth provider.
180-
If provided, defaults to 'code flow' authorization.
181-
If not provided or None, then defaults to 'token flow'
182-
authorization.
183180
redirect_uri (str): The URI to redirect the callback
184181
to
185182
client_secret (str): The client secret of your app. Only needed
186183
if code is not None
187184
resource (str): Defaults to None,The resource
188185
you want to access
189186
"""
190-
# First, default setup of common parameters
191187
params = {
192188
"client_id": self.client_id,
193-
"redirect_uri": redirect_uri
189+
"redirect_uri": redirect_uri,
190+
"client_secret": client_secret,
191+
"code": code,
192+
"response_type": "code"
194193
}
195194

196195
if resource is not None:
197196
params["resource"] = resource
198197

199-
response = None
200-
201-
# Fork based on whether a code was provided. If provided, then redeem the code.
202-
if code is not None:
203-
if client_secret is not None:
204-
params["client_secret"] = client_secret
205-
else:
206-
raise RuntimeError("client_secret must be provided for 'code flow' authorization.")
207-
params["code"] = code
208-
params["response_type"] = "code"
209-
auth_url = self._auth_token_url
210-
headers = {"Content-Type": "application/x-www-form-urlencoded"}
211-
response = self._http_provider.send(method="POST",
212-
headers=headers,
213-
url=auth_url,
214-
data=params)
215-
else:
216-
params["response_type"] = "token"
217-
auth_url = self._auth_server_url
218-
response = self._http_provider.send(method="GET",
219-
url=auth_url,
220-
data=params)
198+
auth_url = self._auth_token_url
199+
headers = {"Content-Type": "application/x-www-form-urlencoded"}
200+
response = self._http_provider.send(method="POST",
201+
headers=headers,
202+
url=auth_url,
203+
data=params)
221204

222205
rcont = json.loads(response.content)
223206
self._session = self._session_type(rcont["token_type"],
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
'''
2+
------------------------------------------------------------------------------
3+
Copyright (c) 2015 Microsoft Corporation
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in
13+
all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.
22+
------------------------------------------------------------------------------
23+
'''
24+
try:
25+
from http.server import HTTPServer, BaseHTTPRequestHandler
26+
except ImportError:
27+
from SimpleHTTPServer import SimpleHTTPRequestHandler as BaseHTTPRequestHandler
28+
from SocketServer import TCPServer as HTTPServer
29+
30+
try:
31+
from urllib.parse import urlparse, parse_qs, unquote
32+
except ImportError:
33+
from urlparse import urlparse, parse_qs
34+
from urllib import unquote
35+
36+
import threading
37+
import webbrowser
38+
39+
40+
def get_access_token(auth_url, redirect_uri):
41+
"""Easy way to get the auth token. Wraps up all the threading
42+
and stuff. Does block main thread.
43+
44+
Args:
45+
auth_url (str): URL of auth server, including query params
46+
needed to get access token.
47+
redirect_uri (str): Redirect URI, as set for the app. Should be
48+
something like "http://localhost:8080" for this to work.
49+
50+
Returns:
51+
str: A string representing the auth code, sent back by the server
52+
"""
53+
url_netloc = urlparse(redirect_uri).netloc
54+
if ':' not in url_netloc:
55+
host_address = url_netloc
56+
port = 80 # default port
57+
else:
58+
host_address, port = url_netloc.split(':')
59+
port = int(port)
60+
# Set up HTTP server and thread
61+
token_acquired = threading.Event()
62+
s = GetAccessTokenServer((host_address, port), token_acquired, GetAccessTokenRequestHandler)
63+
th = threading.Thread(target=s.serve_forever)
64+
th.start()
65+
webbrowser.open(auth_url)
66+
# At this point the browser will open and the code
67+
# will be extracted by the server
68+
token_acquired.wait() # First wait for the response from the auth server
69+
auth_token = s.authentication_token
70+
s.shutdown()
71+
th.join()
72+
return auth_token
73+
74+
75+
class GetAccessTokenServer(HTTPServer, object):
76+
77+
def __init__(self, server_address, stop_event, RequestHandlerClass):
78+
HTTPServer.__init__(self, server_address, RequestHandlerClass)
79+
self._stop_event = stop_event
80+
self._access_token = None
81+
self._authentication_token = None
82+
83+
@property
84+
def access_token(self):
85+
return self._access_token
86+
87+
@access_token.setter
88+
def access_token(self, value):
89+
self._access_token = value
90+
if value is not None:
91+
self._stop_event.set()
92+
93+
@property
94+
def authentication_token(self):
95+
return self._authentication_token
96+
97+
@authentication_token.setter
98+
def authentication_token(self, value):
99+
self._authentication_token = value
100+
if value is not None:
101+
self._stop_event.set()
102+
103+
104+
class GetAccessTokenRequestHandler(BaseHTTPRequestHandler):
105+
106+
def do_GET(self):
107+
params = parse_qs(urlparse(self.path).query)
108+
if "access_token" in params:
109+
# Extract the access token query param
110+
self.server.access_token = params["access_token"][0]
111+
if "authentication_token" in params:
112+
# Extract the auth token query param
113+
self.server.authentication_token = params["authentication_token"][0]
114+
if "error" in params:
115+
error_msg, error_desc = (unquote(params["error"][0]),
116+
unquote(params["error_description"][0]))
117+
raise RuntimeError("The server returned an error: {} - {}"
118+
.format(error_msg, error_desc))
119+
self.send_response(200)
120+
self.send_header("Content-type", "text/html")
121+
self.end_headers()
122+
self.wfile.write(bytes(
123+
'<script type="text/javascript">window.close()</script>'
124+
.encode("utf-8")))

0 commit comments

Comments
 (0)