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

Commit

Permalink
Merge pull request #47 from OneDrive/business-support
Browse files Browse the repository at this point in the history
Business support
  • Loading branch information
cdmayer authored Aug 22, 2016
2 parents 3f56483 + d32ff4c commit 16572ec
Show file tree
Hide file tree
Showing 5 changed files with 441 additions and 29 deletions.
40 changes: 40 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ Next, include the SDK in your Python project by adding:

## Authentication

### OneDrive

To interact with the OneDrive API, your app must authenticate. You can use the following code sample to do so.

```python
Expand Down Expand Up @@ -43,6 +45,9 @@ code = input('Paste code here: ')
client.auth_provider.authenticate(code, redirect_uri, client_secret)
```

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

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

### OneDrive for Business

To interact with the OneDrive API, your app must authenticate for a specific resource. Your
app must first use the Resource Discovery helper to find out which service you can access.
Then, you can build a client to access those resources. This uses a slightly different
auth flow than the standard code flow - note the use of `redeem_refresh_token` with
the `service_resource_id` of the service you want to access.

```python
import onedrivesdk
from onedrivesdk.helpers import GetAuthCodeServer
from onedrivesdk.helpers.resource_discovery import ResourceDiscoveryRequest

redirect_uri = 'http://localhost:8080'
client_id = your_client_id
client_secret = your_client_secret
discovery_uri = 'https://api.office.com/discovery/'
auth_server_url='https://login.microsoftonline.com/common/oauth2/authorize',
auth_token_url='https://login.microsoftonline.com/common/oauth2/token'

http = onedrivesdk.HttpProvider()
auth = onedrivesdk.AuthProvider(http,
client_id,
auth_server_url=auth_server_url,
auth_token_url=auth_token_url)
auth_url = auth.get_auth_url(redirect_uri)
code = GetAuthCodeServer.get_auth_code(auth_url, redirect_uri)
auth.authenticate(code, redirect_uri, client_secret, resource=resource)
# If you have access to more than one service, you'll need to decide
# which ServiceInfo to use instead of just using the first one, as below.
service_info = ResourceDiscoveryRequest().get_service_info(auth.access_token)[0]
auth.redeem_refresh_token(service_info.service_resource_id)
client = onedrivesdk.OneDriveClient(service_info.service_resource_id + '/_api/v2.0/', auth, http)
```

## Examples

**Note:** All examples assume that your app has already been
Expand Down
127 changes: 98 additions & 29 deletions src/onedrivesdk/auth_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,11 @@

class AuthProvider(AuthProviderBase):

AUTH_SERVER_URL = "https://login.live.com/oauth20_authorize.srf"
AUTH_TOKEN_URL = "https://login.live.com/oauth20_token.srf"
MSA_AUTH_SERVER_URL = "https://login.live.com/oauth20_authorize.srf"
MSA_AUTH_TOKEN_URL = "https://login.live.com/oauth20_token.srf"

def __init__(self, http_provider, client_id=None, scopes=None, access_token=None, session_type=None, loop=None):
def __init__(self, http_provider, client_id=None, scopes=None, access_token=None, session_type=None, loop=None,
auth_server_url=None, auth_token_url=None):
"""Initialize the authentication provider for authenticating
requests sent to OneDrive
Expand All @@ -50,8 +51,7 @@ def __init__(self, http_provider, client_id=None, scopes=None, access_token=None
application
scopes (list of str): Defaults to None, the scopes
that are required for your application
access_token (str): Defaults to None, the access token
for your application
access_token (str): Defaults to None. Not used in this implementation.
session_type (:class:`SessionBase<onedrivesdk.session_base.SessionBase>`):
Defaults to :class:`Session<onedrivesdk.session.Session>`,
the implementation of SessionBase that stores your
Expand All @@ -63,13 +63,18 @@ def __init__(self, http_provider, client_id=None, scopes=None, access_token=None
loop to use for all async requests. If none is provided,
asyncio.get_event_loop() will be called. If using Python
3.3 or below this does not need to be specified
auth_server_url (str): URL where OAuth authentication can be performed. If
None, defaults to OAuth for Microsoft Account.
auth_token_url (str): URL where OAuth token can be redeemed. If None,
defaults to OAuth for Microsoft Account.
"""
self._http_provider = http_provider
self._client_id = client_id
self._scopes = scopes
self._access_token = access_token
self._session_type = Session if session_type is None else session_type
self._session = None
self._auth_server_url = self.MSA_AUTH_SERVER_URL if auth_server_url is None else auth_server_url
self._auth_token_url = self.MSA_AUTH_TOKEN_URL if auth_token_url is None else auth_token_url

if sys.version_info >= (3, 4, 0):
import asyncio
Expand Down Expand Up @@ -106,66 +111,95 @@ def scopes(self, value):
@property
def access_token(self):
"""Gets and sets the access_token for the
:class:`AuthProvider`
Returns:
str: The access token. Looks at the session to figure out what the access token is, since this
class does not directly store the access token.
"""
if self._session is not None:
return self._session.access_token
return None

@property
def auth_server_url(self):
"""Gets and sets the authorization server url for the
:class:`AuthProvider`
Returns:
str: Auth server url
"""
return self._auth_server_url

@auth_server_url.setter
def auth_server_url(self, value):
self._auth_server_url = value

@property
def auth_token_url(self):
"""Gets and sets the authorization token url for the
AuthProvider
Returns:
str: The access token
str: The auth token url
"""
return self._access_token
return self._auth_token_url

@access_token.setter
def access_token(self, value):
self._access_token = value
@auth_token_url.setter
def auth_token_url(self, value):
self._auth_token_url = value

def get_auth_url(self, redirect_uri):
def get_auth_url(self, redirect_uri, response_type=None):
"""Build the auth url using the params provided
and the auth_provider
Args:
redirect_uri (str): The URI to redirect the response
to
response_type (str): Response type query param value.
If not provided, defaults to 'code'. Should be either
'code' or 'token'.
"""

params = {
"client_id": self.client_id,
"scope": " ".join(self.scopes),
"response_type": "code",
"response_type": "code" if response_type is None else response_type,
"redirect_uri": redirect_uri
}
return "{}?{}".format(self.AUTH_SERVER_URL, urlencode(params))
if self.scopes is not None:
params["scope"] = " ".join(self.scopes)

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

def authenticate(self, code, redirect_uri, client_secret=None, resource=None):
"""Takes in a code string, gets the access token,
and creates session property bag
def authenticate(self, code, redirect_uri, client_secret, resource=None):
"""Takes in a code, gets the access token and creates a session.
Args:
code (str):
The code provided by the oauth provider
The code provided by the oauth provider.
redirect_uri (str): The URI to redirect the callback
to
client_secret (str): Defaults to None, the client
secret of your app
client_secret (str): The client secret of your app.
resource (str): Defaults to None,The resource
you want to access
"""

params = {
"code": code,
"client_id": self.client_id,
"redirect_uri": redirect_uri,
"grant_type": "authorization_code",
"resource": resource,
"client_secret": client_secret,
"code": code,
"response_type": "code",
"grant_type": "authorization_code"
}

if client_secret is not None:
params["client_secret"] = client_secret
if resource is not None:
params["resource"] = resource

auth_url = self._auth_token_url
headers = {"Content-Type": "application/x-www-form-urlencoded"}
response = self._http_provider.send(method="POST",
headers=headers,
url=self.AUTH_TOKEN_URL,
url=auth_url,
data=params)

rcont = json.loads(response.content)
Expand All @@ -174,7 +208,7 @@ def authenticate(self, code, redirect_uri, client_secret=None, resource=None):
rcont["scope"],
rcont["access_token"],
self.client_id,
self.AUTH_TOKEN_URL,
self._auth_token_url,
redirect_uri,
rcont["refresh_token"] if "refresh_token" in rcont else None,
client_secret)
Expand Down Expand Up @@ -231,6 +265,41 @@ def refresh_token(self):
rcont["access_token"],
rcont["refresh_token"])

def redeem_refresh_token(self, resource):
"""Redeem a refresh token against a new resource. Used
only by OneDrive for Business apps.
Args:
resource (str): URL to resource to be accessed.
Can be a 'serviceResourceId' value obtained from
Discovery Service."""
if self._session is None:
raise RuntimeError("""Session must be authenticated
before refreshing token.""")

if self._session.refresh_token is None:
raise RuntimeError("""Refresh token not present.""")

params = {
"client_id": self._session.client_id,
"redirect_uri": self._session.redirect_uri,
"client_secret": self._session.client_secret,
"refresh_token": self._session.refresh_token,
"grant_type": "refresh_token",
"resource": resource
}

headers = {"Content-Type": "application/x-www-form-urlencoded"}
response = self._http_provider.send(method="POST",
headers=headers,
url=self.auth_token_url,
data=params)
rcont = json.loads(response.content)
self._session.refresh_session(rcont["expires_in"],
"",
rcont["access_token"],
rcont["refresh_token"])

def save_session(self, **save_session_kwargs):
"""Save the current session. Must have already
obtained an access_token.
Expand Down
41 changes: 41 additions & 0 deletions src/onedrivesdk/extensions/onedrivesdk_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,22 @@


def get_default_client(client_id, scopes):
"""Deprecated. Proxy of :method:`get_consumer_client()`.
Get a client using the default HttpProvider and
AuthProvider classes.
Args:
client_id (str): The client id for your application
scopes (list of str): The scopes required for your
application
Returns:
:class:`OneDriveClient<onedrivesdk.requests.one_drive_client.OneDriveClient>`:
A OneDriveClient for making OneDrive requests.
"""
return get_consumer_client(client_id, scopes)

def get_consumer_client(client_id, scopes):
"""Get a client using the default HttpProvider and
AuthProvider classes
Expand All @@ -23,3 +39,28 @@ def get_default_client(client_id, scopes):
return OneDriveClient("https://api.onedrive.com/v1.0/",
auth_provider,
http_provider)


def get_business_client(client_id, scopes, base_url):
"""Get a client using the default HttpProvider and
AuthProvider classes
Args:
client_id (str): The client id for your application
scopes (list of str): The scopes required for your
application
base_url (str): Base URL of OneDrive for Business tenant.
For example, "https://my-sharepoint.contoso.com/v1.0/"
Returns:
:class:`OneDriveClient<onedrivesdk.requests.one_drive_client.OneDriveClient>`:
A OneDriveClient for making OneDrive requests.
"""
http_provider = HttpProvider()
auth_provider = AuthProvider(http_provider=http_provider,
client_id=client_id,
scopes=scopes)
return OneDriveClient(base_url,
auth_provider,
http_provider)

Loading

0 comments on commit 16572ec

Please sign in to comment.