|
1 | | -import uuid |
2 | | -import requests |
3 | 1 | from flask import Flask, render_template, session, request, redirect, url_for |
4 | 2 | from flask_session import Session # https://pythonhosted.org/Flask-Session |
5 | | -import msal |
| 3 | +import identity, identity.web |
| 4 | +import requests |
6 | 5 | import app_config |
7 | 6 |
|
8 | 7 |
|
|
17 | 16 | from werkzeug.middleware.proxy_fix import ProxyFix |
18 | 17 | app.wsgi_app = ProxyFix(app.wsgi_app, x_proto=1, x_host=1) |
19 | 18 |
|
20 | | -@app.route("/") |
21 | | -def index(): |
22 | | - if not session.get("user"): |
23 | | - return redirect(url_for("login")) |
24 | | - return render_template('index.html', user=session["user"], version=msal.__version__) |
| 19 | +auth = identity.web.Auth( |
| 20 | + session=session, |
| 21 | + authority=app.config.get("AUTHORITY"), |
| 22 | + client_id=app.config["CLIENT_ID"], |
| 23 | + client_credential=app.config["CLIENT_SECRET"], |
| 24 | + ) |
25 | 25 |
|
26 | 26 | @app.route("/login") |
27 | 27 | def login(): |
28 | | - # Technically we could use empty list [] as scopes to do just sign in, |
29 | | - # here we choose to also collect end user consent upfront |
30 | | - session["flow"] = _build_auth_code_flow(scopes=app_config.SCOPE) |
31 | | - return render_template("login.html", auth_url=session["flow"]["auth_uri"], version=msal.__version__) |
| 28 | + return render_template("login.html", version=identity.__version__, **auth.log_in( |
| 29 | + scopes=app_config.SCOPE, # Have user consent scopes during log-in |
| 30 | + redirect_uri=url_for("auth_response", _external=True), # Optional. If present, this absolute URL must match your app's redirect_uri registered in Azure Portal |
| 31 | + )) |
32 | 32 |
|
33 | | -@app.route(app_config.REDIRECT_PATH) # Its absolute URL must match your app's redirect_uri set in AAD |
34 | | -def authorized(): |
35 | | - try: |
36 | | - cache = _load_cache() |
37 | | - result = _build_msal_app(cache=cache).acquire_token_by_auth_code_flow( |
38 | | - session.get("flow", {}), request.args) |
39 | | - if "error" in result: |
40 | | - return render_template("auth_error.html", result=result) |
41 | | - session["user"] = result.get("id_token_claims") |
42 | | - _save_cache(cache) |
43 | | - except ValueError: # Usually caused by CSRF |
44 | | - pass # Simply ignore them |
45 | | - return redirect(url_for("index")) |
| 33 | +@app.route(app_config.REDIRECT_PATH) |
| 34 | +def auth_response(): |
| 35 | + result = auth.complete_log_in(request.args) |
| 36 | + return render_template("auth_error.html", result=result) if "error" in result else redirect(url_for("index")) |
46 | 37 |
|
47 | 38 | @app.route("/logout") |
48 | 39 | def logout(): |
49 | | - session.clear() # Wipe out user and its token cache from session |
50 | | - return redirect( # Also logout from your tenant's web session |
51 | | - app_config.AUTHORITY + "/oauth2/v2.0/logout" + |
52 | | - "?post_logout_redirect_uri=" + url_for("index", _external=True)) |
| 40 | + return redirect(auth.log_out(url_for("index", _external=True))) |
| 41 | + |
| 42 | +@app.route("/") |
| 43 | +def index(): |
| 44 | + if not auth.get_user(): |
| 45 | + return redirect(url_for("login")) |
| 46 | + return render_template('index.html', user=auth.get_user(), version=identity.__version__) |
53 | 47 |
|
54 | | -@app.route("/graphcall") |
55 | | -def graphcall(): |
56 | | - token = _get_token_from_cache(app_config.SCOPE) |
57 | | - if not token: |
| 48 | +@app.route("/call_downstream_api") |
| 49 | +def call_downstream_api(): |
| 50 | + token = auth.get_token_for_user(app_config.SCOPE) |
| 51 | + if "error" in token: |
58 | 52 | return redirect(url_for("login")) |
59 | | - graph_data = requests.get( # Use token to call downstream service |
| 53 | + api_result = requests.get( # Use token to call downstream api |
60 | 54 | app_config.ENDPOINT, |
61 | 55 | headers={'Authorization': 'Bearer ' + token['access_token']}, |
62 | 56 | ).json() |
63 | | - return render_template('display.html', result=graph_data) |
64 | | - |
65 | | - |
66 | | -def _load_cache(): |
67 | | - cache = msal.SerializableTokenCache() |
68 | | - if session.get("token_cache"): |
69 | | - cache.deserialize(session["token_cache"]) |
70 | | - return cache |
71 | | - |
72 | | -def _save_cache(cache): |
73 | | - if cache.has_state_changed: |
74 | | - session["token_cache"] = cache.serialize() |
75 | | - |
76 | | -def _build_msal_app(cache=None, authority=None): |
77 | | - return msal.ConfidentialClientApplication( |
78 | | - app_config.CLIENT_ID, authority=authority or app_config.AUTHORITY, |
79 | | - client_credential=app_config.CLIENT_SECRET, token_cache=cache) |
80 | | - |
81 | | -def _build_auth_code_flow(authority=None, scopes=None): |
82 | | - return _build_msal_app(authority=authority).initiate_auth_code_flow( |
83 | | - scopes or [], |
84 | | - redirect_uri=url_for("authorized", _external=True)) |
85 | | - |
86 | | -def _get_token_from_cache(scope=None): |
87 | | - cache = _load_cache() # This web app maintains one cache per session |
88 | | - cca = _build_msal_app(cache=cache) |
89 | | - accounts = cca.get_accounts() |
90 | | - if accounts: # So all account(s) belong to the current signed-in user |
91 | | - result = cca.acquire_token_silent(scope, account=accounts[0]) |
92 | | - _save_cache(cache) |
93 | | - return result |
94 | | - |
95 | | -app.jinja_env.globals.update(_build_auth_code_flow=_build_auth_code_flow) # Used in template |
| 57 | + return render_template('display.html', result=api_result) |
96 | 58 |
|
97 | 59 | if __name__ == "__main__": |
98 | 60 | app.run() |
|
0 commit comments