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

Commit 16572ec

Browse files
authored
Merge pull request #47 from OneDrive/business-support
Business support
2 parents 3f56483 + d32ff4c commit 16572ec

File tree

5 files changed

+441
-29
lines changed

5 files changed

+441
-29
lines changed

README.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ Next, include the SDK in your Python project by adding:
1515

1616
## Authentication
1717

18+
### OneDrive
19+
1820
To interact with the OneDrive API, your app must authenticate. You can use the following code sample to do so.
1921

2022
```python
@@ -43,6 +45,9 @@ code = input('Paste code here: ')
4345
client.auth_provider.authenticate(code, redirect_uri, client_secret)
4446
```
4547

48+
The above code requires copy-pasting into your browser and back into your console. If you want to remove some of
49+
that manual work, you can use the helper class `GetAuthCodeServer`. That helper class spins up a webserver, so
50+
this method cannot be used on all environments.
4651

4752
```python
4853
import onedrivesdk
@@ -65,6 +70,41 @@ client.auth_provider.authenticate(code, redirect_uri, client_secret)
6570
Once your app is authenticated, you should have access to the OneDrive API, and
6671
can begin making calls using the SDK.
6772

73+
### OneDrive for Business
74+
75+
To interact with the OneDrive API, your app must authenticate for a specific resource. Your
76+
app must first use the Resource Discovery helper to find out which service you can access.
77+
Then, you can build a client to access those resources. This uses a slightly different
78+
auth flow than the standard code flow - note the use of `redeem_refresh_token` with
79+
the `service_resource_id` of the service you want to access.
80+
81+
```python
82+
import onedrivesdk
83+
from onedrivesdk.helpers import GetAuthCodeServer
84+
from onedrivesdk.helpers.resource_discovery import ResourceDiscoveryRequest
85+
86+
redirect_uri = 'http://localhost:8080'
87+
client_id = your_client_id
88+
client_secret = your_client_secret
89+
discovery_uri = 'https://api.office.com/discovery/'
90+
auth_server_url='https://login.microsoftonline.com/common/oauth2/authorize',
91+
auth_token_url='https://login.microsoftonline.com/common/oauth2/token'
92+
93+
http = onedrivesdk.HttpProvider()
94+
auth = onedrivesdk.AuthProvider(http,
95+
client_id,
96+
auth_server_url=auth_server_url,
97+
auth_token_url=auth_token_url)
98+
auth_url = auth.get_auth_url(redirect_uri)
99+
code = GetAuthCodeServer.get_auth_code(auth_url, redirect_uri)
100+
auth.authenticate(code, redirect_uri, client_secret, resource=resource)
101+
# If you have access to more than one service, you'll need to decide
102+
# which ServiceInfo to use instead of just using the first one, as below.
103+
service_info = ResourceDiscoveryRequest().get_service_info(auth.access_token)[0]
104+
auth.redeem_refresh_token(service_info.service_resource_id)
105+
client = onedrivesdk.OneDriveClient(service_info.service_resource_id + '/_api/v2.0/', auth, http)
106+
```
107+
68108
## Examples
69109

70110
**Note:** All examples assume that your app has already been

src/onedrivesdk/auth_provider.py

Lines changed: 98 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,11 @@
3636

3737
class AuthProvider(AuthProviderBase):
3838

39-
AUTH_SERVER_URL = "https://login.live.com/oauth20_authorize.srf"
40-
AUTH_TOKEN_URL = "https://login.live.com/oauth20_token.srf"
39+
MSA_AUTH_SERVER_URL = "https://login.live.com/oauth20_authorize.srf"
40+
MSA_AUTH_TOKEN_URL = "https://login.live.com/oauth20_token.srf"
4141

42-
def __init__(self, http_provider, client_id=None, scopes=None, access_token=None, session_type=None, loop=None):
42+
def __init__(self, http_provider, client_id=None, scopes=None, access_token=None, session_type=None, loop=None,
43+
auth_server_url=None, auth_token_url=None):
4344
"""Initialize the authentication provider for authenticating
4445
requests sent to OneDrive
4546
@@ -50,8 +51,7 @@ def __init__(self, http_provider, client_id=None, scopes=None, access_token=None
5051
application
5152
scopes (list of str): Defaults to None, the scopes
5253
that are required for your application
53-
access_token (str): Defaults to None, the access token
54-
for your application
54+
access_token (str): Defaults to None. Not used in this implementation.
5555
session_type (:class:`SessionBase<onedrivesdk.session_base.SessionBase>`):
5656
Defaults to :class:`Session<onedrivesdk.session.Session>`,
5757
the implementation of SessionBase that stores your
@@ -63,13 +63,18 @@ def __init__(self, http_provider, client_id=None, scopes=None, access_token=None
6363
loop to use for all async requests. If none is provided,
6464
asyncio.get_event_loop() will be called. If using Python
6565
3.3 or below this does not need to be specified
66+
auth_server_url (str): URL where OAuth authentication can be performed. If
67+
None, defaults to OAuth for Microsoft Account.
68+
auth_token_url (str): URL where OAuth token can be redeemed. If None,
69+
defaults to OAuth for Microsoft Account.
6670
"""
6771
self._http_provider = http_provider
6872
self._client_id = client_id
6973
self._scopes = scopes
70-
self._access_token = access_token
7174
self._session_type = Session if session_type is None else session_type
7275
self._session = None
76+
self._auth_server_url = self.MSA_AUTH_SERVER_URL if auth_server_url is None else auth_server_url
77+
self._auth_token_url = self.MSA_AUTH_TOKEN_URL if auth_token_url is None else auth_token_url
7378

7479
if sys.version_info >= (3, 4, 0):
7580
import asyncio
@@ -106,66 +111,95 @@ def scopes(self, value):
106111
@property
107112
def access_token(self):
108113
"""Gets and sets the access_token for the
114+
:class:`AuthProvider`
115+
116+
Returns:
117+
str: The access token. Looks at the session to figure out what the access token is, since this
118+
class does not directly store the access token.
119+
"""
120+
if self._session is not None:
121+
return self._session.access_token
122+
return None
123+
124+
@property
125+
def auth_server_url(self):
126+
"""Gets and sets the authorization server url for the
127+
:class:`AuthProvider`
128+
129+
Returns:
130+
str: Auth server url
131+
"""
132+
return self._auth_server_url
133+
134+
@auth_server_url.setter
135+
def auth_server_url(self, value):
136+
self._auth_server_url = value
137+
138+
@property
139+
def auth_token_url(self):
140+
"""Gets and sets the authorization token url for the
109141
AuthProvider
110142
111143
Returns:
112-
str: The access token
144+
str: The auth token url
113145
"""
114-
return self._access_token
146+
return self._auth_token_url
115147

116-
@access_token.setter
117-
def access_token(self, value):
118-
self._access_token = value
148+
@auth_token_url.setter
149+
def auth_token_url(self, value):
150+
self._auth_token_url = value
119151

120-
def get_auth_url(self, redirect_uri):
152+
def get_auth_url(self, redirect_uri, response_type=None):
121153
"""Build the auth url using the params provided
122154
and the auth_provider
123155
124156
Args:
125157
redirect_uri (str): The URI to redirect the response
126158
to
159+
response_type (str): Response type query param value.
160+
If not provided, defaults to 'code'. Should be either
161+
'code' or 'token'.
127162
"""
128163

129164
params = {
130165
"client_id": self.client_id,
131-
"scope": " ".join(self.scopes),
132-
"response_type": "code",
166+
"response_type": "code" if response_type is None else response_type,
133167
"redirect_uri": redirect_uri
134168
}
135-
return "{}?{}".format(self.AUTH_SERVER_URL, urlencode(params))
169+
if self.scopes is not None:
170+
params["scope"] = " ".join(self.scopes)
171+
172+
return "{}?{}".format(self._auth_server_url, urlencode(params))
136173

137-
def authenticate(self, code, redirect_uri, client_secret=None, resource=None):
138-
"""Takes in a code string, gets the access token,
139-
and creates session property bag
174+
def authenticate(self, code, redirect_uri, client_secret, resource=None):
175+
"""Takes in a code, gets the access token and creates a session.
140176
141177
Args:
142178
code (str):
143-
The code provided by the oauth provider
179+
The code provided by the oauth provider.
144180
redirect_uri (str): The URI to redirect the callback
145181
to
146-
client_secret (str): Defaults to None, the client
147-
secret of your app
182+
client_secret (str): The client secret of your app.
148183
resource (str): Defaults to None,The resource
149184
you want to access
150185
"""
151-
152186
params = {
153-
"code": code,
154187
"client_id": self.client_id,
155188
"redirect_uri": redirect_uri,
156-
"grant_type": "authorization_code",
157-
"resource": resource,
189+
"client_secret": client_secret,
190+
"code": code,
191+
"response_type": "code",
192+
"grant_type": "authorization_code"
158193
}
159194

160-
if client_secret is not None:
161-
params["client_secret"] = client_secret
162195
if resource is not None:
163196
params["resource"] = resource
164197

198+
auth_url = self._auth_token_url
165199
headers = {"Content-Type": "application/x-www-form-urlencoded"}
166200
response = self._http_provider.send(method="POST",
167201
headers=headers,
168-
url=self.AUTH_TOKEN_URL,
202+
url=auth_url,
169203
data=params)
170204

171205
rcont = json.loads(response.content)
@@ -174,7 +208,7 @@ def authenticate(self, code, redirect_uri, client_secret=None, resource=None):
174208
rcont["scope"],
175209
rcont["access_token"],
176210
self.client_id,
177-
self.AUTH_TOKEN_URL,
211+
self._auth_token_url,
178212
redirect_uri,
179213
rcont["refresh_token"] if "refresh_token" in rcont else None,
180214
client_secret)
@@ -231,6 +265,41 @@ def refresh_token(self):
231265
rcont["access_token"],
232266
rcont["refresh_token"])
233267

268+
def redeem_refresh_token(self, resource):
269+
"""Redeem a refresh token against a new resource. Used
270+
only by OneDrive for Business apps.
271+
272+
Args:
273+
resource (str): URL to resource to be accessed.
274+
Can be a 'serviceResourceId' value obtained from
275+
Discovery Service."""
276+
if self._session is None:
277+
raise RuntimeError("""Session must be authenticated
278+
before refreshing token.""")
279+
280+
if self._session.refresh_token is None:
281+
raise RuntimeError("""Refresh token not present.""")
282+
283+
params = {
284+
"client_id": self._session.client_id,
285+
"redirect_uri": self._session.redirect_uri,
286+
"client_secret": self._session.client_secret,
287+
"refresh_token": self._session.refresh_token,
288+
"grant_type": "refresh_token",
289+
"resource": resource
290+
}
291+
292+
headers = {"Content-Type": "application/x-www-form-urlencoded"}
293+
response = self._http_provider.send(method="POST",
294+
headers=headers,
295+
url=self.auth_token_url,
296+
data=params)
297+
rcont = json.loads(response.content)
298+
self._session.refresh_session(rcont["expires_in"],
299+
"",
300+
rcont["access_token"],
301+
rcont["refresh_token"])
302+
234303
def save_session(self, **save_session_kwargs):
235304
"""Save the current session. Must have already
236305
obtained an access_token.

src/onedrivesdk/extensions/onedrivesdk_helper.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,22 @@
44

55

66
def get_default_client(client_id, scopes):
7+
"""Deprecated. Proxy of :method:`get_consumer_client()`.
8+
Get a client using the default HttpProvider and
9+
AuthProvider classes.
10+
11+
Args:
12+
client_id (str): The client id for your application
13+
scopes (list of str): The scopes required for your
14+
application
15+
16+
Returns:
17+
:class:`OneDriveClient<onedrivesdk.requests.one_drive_client.OneDriveClient>`:
18+
A OneDriveClient for making OneDrive requests.
19+
"""
20+
return get_consumer_client(client_id, scopes)
21+
22+
def get_consumer_client(client_id, scopes):
723
"""Get a client using the default HttpProvider and
824
AuthProvider classes
925
@@ -23,3 +39,28 @@ def get_default_client(client_id, scopes):
2339
return OneDriveClient("https://api.onedrive.com/v1.0/",
2440
auth_provider,
2541
http_provider)
42+
43+
44+
def get_business_client(client_id, scopes, base_url):
45+
"""Get a client using the default HttpProvider and
46+
AuthProvider classes
47+
48+
Args:
49+
client_id (str): The client id for your application
50+
scopes (list of str): The scopes required for your
51+
application
52+
base_url (str): Base URL of OneDrive for Business tenant.
53+
For example, "https://my-sharepoint.contoso.com/v1.0/"
54+
55+
Returns:
56+
:class:`OneDriveClient<onedrivesdk.requests.one_drive_client.OneDriveClient>`:
57+
A OneDriveClient for making OneDrive requests.
58+
"""
59+
http_provider = HttpProvider()
60+
auth_provider = AuthProvider(http_provider=http_provider,
61+
client_id=client_id,
62+
scopes=scopes)
63+
return OneDriveClient(base_url,
64+
auth_provider,
65+
http_provider)
66+

0 commit comments

Comments
 (0)