Last active
July 7, 2019 14:32
-
-
Save toyg/fa55d4b18f872c8f77dd58c9482acee2 to your computer and use it in GitHub Desktop.
Example Flask app showing how to manage a webapp-style authentication flow with python-o365(once/if my PR is accepted - in meantime, can be used with toyg/python-0365 on the webapp_flow branch)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# when testing locally, set environment variable OAUTHLIB_INSECURE_TRANSPORT to 1 | |
# so you can use http://localhost... in redirect | |
import os | |
from O365 import Connection | |
from flask import Flask, redirect, request, url_for, abort | |
# O365 secrets | |
APP_ID = os.environ.get('O365_APP_ID') | |
APP_PWD = os.environ.get('O365_APP_PWD') | |
TENANT_ID = os.environ.get('O365_TENANT_ID') | |
if not APP_ID or not APP_PWD: | |
print("You must supply O365_APP_ID and O365_APP_PWD as environment variables") | |
exit(1) | |
APP_CREDENTIALS = (APP_ID, APP_PWD) | |
# flask config | |
app = Flask(__name__) | |
app.secret_key = os.environ.get('FLASK_SEC_KEY') | |
app.config['SERVER_NAME'] = os.environ.get('FLASK_DOMAIN') # e.g. localhost:8000 | |
# transient cache of in-flow requests | |
_oauth_inflow_requests = {} | |
@app.route('/') | |
def start_0365_oauth(): | |
""" | |
Flow entry-point, that will redirect to MS login page | |
and save the state. | |
""" | |
scopes = ["https://graph.microsoft.com/Calendars.Read", | |
'offline_access'] | |
acceptance_url = url_for('complete_oauth', _external=True) | |
conn = Connection(APP_CREDENTIALS, | |
tenant_id=TENANT_ID, | |
redirect_uri=acceptance_url, | |
scopes=scopes) | |
# start the flow | |
url, state, redirect_uri, scopes = conn.get_authorization_webflow( | |
redirect_uri=acceptance_url, | |
requested_scopes=scopes) | |
# save the resulting details so that we can recreate the session later | |
_oauth_inflow_requests[state] = (url, redirect_uri, scopes) | |
# send user to MS | |
return redirect(url) | |
@app.route('/oauth_accepted') | |
def complete_oauth(): | |
""" post-authentication/authorization callback.""" | |
try: | |
# retrieve the details for this session | |
state = request.args.get('state') | |
url, redirect_url, scopes = _oauth_inflow_requests[state] | |
# rebuild our connection | |
conn = Connection(APP_CREDENTIALS, scopes=scopes, tenant_id=TENANT_ID) | |
conn.recreate_session(state, redirect_uri=redirect_url, scopes=scopes) | |
# fetch the actual token, finally | |
result = conn.request_token(authorization_url=request.url, | |
state=state, | |
redirect_uri=redirect_url) | |
# if true, we have an access token | |
if result == True: | |
# forget the session state | |
_oauth_inflow_requests.pop(state) | |
# now you have a working Connection, so you could do something like: | |
# schedule = Schedule(con=conn, protocol=MSGraphProtocol()) | |
# return "<br>".join(schedule.list_calendars()) | |
# etc etc | |
return str(result) | |
except TypeError as e: | |
app.logger.error("Called oauth acceptance url without state") | |
abort(400) | |
except KeyError as e: | |
app.logger.error("Called oauth acceptance url with invalid state: {s}".format(s=e.args[0])) | |
abort(400) | |
if __name__ == '__main__': | |
app.run() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment