Skip to content

Instantly share code, notes, and snippets.

@toyg
Last active July 7, 2019 14:32
Show Gist options
  • Save toyg/fa55d4b18f872c8f77dd58c9482acee2 to your computer and use it in GitHub Desktop.
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)
# 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