From d09bcfb83ee6dd9e7871c0a9ec49d32094d3aa5a Mon Sep 17 00:00:00 2001 From: Abhidnya Patil Date: Thu, 11 Apr 2019 00:14:20 -0700 Subject: [PATCH 1/8] Adding sample for certificate credential flow --- sample/certificate_credential_sample.py | 65 +++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 sample/certificate_credential_sample.py diff --git a/sample/certificate_credential_sample.py b/sample/certificate_credential_sample.py new file mode 100644 index 00000000..d24a1dd4 --- /dev/null +++ b/sample/certificate_credential_sample.py @@ -0,0 +1,65 @@ +""" +The configuration file would look like this: + +{ + "authority": "https://login.microsoftonline.com/organizations", + "client_id": "your_client_id", + "scope": ["https://graph.microsoft.com/.default"], + "thumbprint": "" + "privateKeyFile": "" +} + +You can then run this sample with a JSON configuration file: + + python sample.py parameters.json +""" + +import sys # For simplicity, we'll read config file from 1st CLI param sys.argv[1] +import json +import logging + +import msal + + +# Optional logging +# logging.basicConfig(level=logging.DEBUG) + +def get_private_key(filename): + with open(filename, 'r') as pem_file: + private_pem = pem_file.read() + return private_pem + + +config = json.load(open(sys.argv[1])) + +# Create a preferably long-lived app instance which maintains a token cache. +app = msal.ConfidentialClientApplication( + config["client_id"], authority=config["authority"], + client_credential={"thumbprint": config["thumbprint"], "private_key": get_private_key(config['privateKeyFile'])} + # token_cache=... # Default cache is in memory only. + # You can learn how to use SerializableTokenCache from + # https://msal-python.rtfd.io/en/latest/#msal.SerializableTokenCache + ) + +# The pattern to acquire a token looks like this. +result = None + +# Firstly, looks up a token from cache +# Since we are looking for token for the current app, NOT for an end user, +# notice we give account parameter as None. +result = app.acquire_token_silent(config["scope"], account=None) + +if not result: + logging.info("No suitable token exists in cache. Let's get a new one from AAD.") + result = app.acquire_token_for_client(scopes=config["scope"]) + +if "access_token" in result: + print(result["access_token"]) + print(result["token_type"]) + print(result["expires_in"]) # You don't normally need to care about this. + # It will be good for at least 5 minutes. +else: + print(result.get("error")) + print(result.get("error_description")) + print(result.get("correlation_id")) # You may need this when reporting a bug + From 3dab324b873dc463b7cb2bed5580d7a3e4723591 Mon Sep 17 00:00:00 2001 From: Abhidnya Patil Date: Fri, 12 Apr 2019 10:29:14 -0700 Subject: [PATCH 2/8] Adding links to wiki page and minor changes --- sample/certificate_credential_sample.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/sample/certificate_credential_sample.py b/sample/certificate_credential_sample.py index d24a1dd4..040dbcb4 100644 --- a/sample/certificate_credential_sample.py +++ b/sample/certificate_credential_sample.py @@ -5,9 +5,11 @@ "authority": "https://login.microsoftonline.com/organizations", "client_id": "your_client_id", "scope": ["https://graph.microsoft.com/.default"], - "thumbprint": "" - "privateKeyFile": "" + "thumbprint": "790E20ED5C5BF7F44..." + "private_key_file": "filename.pem" } +For mor information about generating thumbprint and private key file, refer to: +https://github.com/AzureAD/microsoft-authentication-library-for-python/wiki/Client-credentials You can then run this sample with a JSON configuration file: @@ -24,18 +26,12 @@ # Optional logging # logging.basicConfig(level=logging.DEBUG) -def get_private_key(filename): - with open(filename, 'r') as pem_file: - private_pem = pem_file.read() - return private_pem - - config = json.load(open(sys.argv[1])) # Create a preferably long-lived app instance which maintains a token cache. app = msal.ConfidentialClientApplication( config["client_id"], authority=config["authority"], - client_credential={"thumbprint": config["thumbprint"], "private_key": get_private_key(config['privateKeyFile'])} + client_credential={"thumbprint": config["thumbprint"], "private_key": open(config['private_key_file']).read()} # token_cache=... # Default cache is in memory only. # You can learn how to use SerializableTokenCache from # https://msal-python.rtfd.io/en/latest/#msal.SerializableTokenCache From 0469f012aa2fd74c1019e14c1d2cb1009bf5db0d Mon Sep 17 00:00:00 2001 From: Abhidnya Patil Date: Tue, 23 Apr 2019 10:53:16 -0700 Subject: [PATCH 3/8] Adding authorization code flow sample --- .../authorization_code_flow_sample.py | 80 +++++++++++++++++++ .../templates/display.html | 19 +++++ 2 files changed, 99 insertions(+) create mode 100644 sample/authorization-code-flow-sample/authorization_code_flow_sample.py create mode 100644 sample/authorization-code-flow-sample/templates/display.html diff --git a/sample/authorization-code-flow-sample/authorization_code_flow_sample.py b/sample/authorization-code-flow-sample/authorization_code_flow_sample.py new file mode 100644 index 00000000..5a5b0a76 --- /dev/null +++ b/sample/authorization-code-flow-sample/authorization_code_flow_sample.py @@ -0,0 +1,80 @@ +""" +The configuration file would look like this: + +{ + "authority": "https://login.microsoftonline.com/organizations", + "client_id": "your_client_id", + "scope": ["https://graph.microsoft.com/.default"] + "client_secret": "yoursecret" +} + +You can then run this sample with a JSON configuration file: + + python sample.py parameters.json +""" + +import sys # For simplicity, we'll read config file from 1st CLI param sys.argv[1] +import json +import logging +import uuid + +import flask as flask + +import msal + +app = flask.Flask(__name__) +app.debug = True +app.secret_key = 'development' + + +# Optional logging +# logging.basicConfig(level=logging.DEBUG) + +config = json.load(open(sys.argv[1])) + +application = msal.ConfidentialClientApplication( + config["client_id"], authority=config["authority"], + client_credential=config["client_secret"], + # token_cache=... # Default cache is in memory only. + # You can learn how to use SerializableTokenCache from + # https://msal-python.rtfd.io/en/latest/#msal.SerializableTokenCache + ) + + +@app.route("/") +def main(): + login_url = 'http://localhost:5000/login' + resp = flask.Response(status=307) + resp.headers['location'] = login_url + return resp + + +@app.route("/login") +def login(): + auth_state = str(uuid.uuid4()) + flask.session['state'] = auth_state + authorization_url = application.get_authorization_request_url(config['scope'], state=auth_state, + redirect_uri=config['redirect_uri']) + resp = flask.Response(status=307) + resp.headers['location'] = authorization_url + return resp + + +@app.route("/getAToken") +def main_logic(): + code = flask.request.args['code'] + state = flask.request.args['state'] + if state != flask.session['state']: + raise ValueError("State does not match") + + result = application.acquire_token_silent(config["scope"], account=None) + + if not result: + logging.info("No suitable token exists in cache. Let's get a new one from AAD.") + result = application.acquire_token_by_authorization_code(code, scopes=config["scope"], + redirect_uri=config['redirect_uri']) + return flask.render_template('display.html', graph_data=result) + + +if __name__ == "__main__": + app.run() diff --git a/sample/authorization-code-flow-sample/templates/display.html b/sample/authorization-code-flow-sample/templates/display.html new file mode 100644 index 00000000..4553fc1b --- /dev/null +++ b/sample/authorization-code-flow-sample/templates/display.html @@ -0,0 +1,19 @@ + + + + + Here is the data from Microsoft Graph !! + + +User Data: + +{% for key, value in graph_data.items() %} + + + + +{% endfor %} +
{{ key }} {{ value }}
+ + + \ No newline at end of file From a3d0eeddcea38294449a0587c2a6e0dfc49522e9 Mon Sep 17 00:00:00 2001 From: Abhidnya Patil Date: Tue, 23 Apr 2019 11:05:05 -0700 Subject: [PATCH 4/8] Changes in variable names and deleting extra files --- .../authorization_code_flow_sample.py | 2 +- .../templates/display.html | 4 +- sample/certificate_credential_sample.py | 61 ------------------- 3 files changed, 3 insertions(+), 64 deletions(-) delete mode 100644 sample/certificate_credential_sample.py diff --git a/sample/authorization-code-flow-sample/authorization_code_flow_sample.py b/sample/authorization-code-flow-sample/authorization_code_flow_sample.py index 5a5b0a76..e374064e 100644 --- a/sample/authorization-code-flow-sample/authorization_code_flow_sample.py +++ b/sample/authorization-code-flow-sample/authorization_code_flow_sample.py @@ -73,7 +73,7 @@ def main_logic(): logging.info("No suitable token exists in cache. Let's get a new one from AAD.") result = application.acquire_token_by_authorization_code(code, scopes=config["scope"], redirect_uri=config['redirect_uri']) - return flask.render_template('display.html', graph_data=result) + return flask.render_template('display.html', auth_result=result) if __name__ == "__main__": diff --git a/sample/authorization-code-flow-sample/templates/display.html b/sample/authorization-code-flow-sample/templates/display.html index 4553fc1b..a82e8bee 100644 --- a/sample/authorization-code-flow-sample/templates/display.html +++ b/sample/authorization-code-flow-sample/templates/display.html @@ -2,12 +2,12 @@ - Here is the data from Microsoft Graph !! + Acquire Token Result User Data: -{% for key, value in graph_data.items() %} +{% for key, value in auth_result.items() %} diff --git a/sample/certificate_credential_sample.py b/sample/certificate_credential_sample.py deleted file mode 100644 index 040dbcb4..00000000 --- a/sample/certificate_credential_sample.py +++ /dev/null @@ -1,61 +0,0 @@ -""" -The configuration file would look like this: - -{ - "authority": "https://login.microsoftonline.com/organizations", - "client_id": "your_client_id", - "scope": ["https://graph.microsoft.com/.default"], - "thumbprint": "790E20ED5C5BF7F44..." - "private_key_file": "filename.pem" -} -For mor information about generating thumbprint and private key file, refer to: -https://github.com/AzureAD/microsoft-authentication-library-for-python/wiki/Client-credentials - -You can then run this sample with a JSON configuration file: - - python sample.py parameters.json -""" - -import sys # For simplicity, we'll read config file from 1st CLI param sys.argv[1] -import json -import logging - -import msal - - -# Optional logging -# logging.basicConfig(level=logging.DEBUG) - -config = json.load(open(sys.argv[1])) - -# Create a preferably long-lived app instance which maintains a token cache. -app = msal.ConfidentialClientApplication( - config["client_id"], authority=config["authority"], - client_credential={"thumbprint": config["thumbprint"], "private_key": open(config['private_key_file']).read()} - # token_cache=... # Default cache is in memory only. - # You can learn how to use SerializableTokenCache from - # https://msal-python.rtfd.io/en/latest/#msal.SerializableTokenCache - ) - -# The pattern to acquire a token looks like this. -result = None - -# Firstly, looks up a token from cache -# Since we are looking for token for the current app, NOT for an end user, -# notice we give account parameter as None. -result = app.acquire_token_silent(config["scope"], account=None) - -if not result: - logging.info("No suitable token exists in cache. Let's get a new one from AAD.") - result = app.acquire_token_for_client(scopes=config["scope"]) - -if "access_token" in result: - print(result["access_token"]) - print(result["token_type"]) - print(result["expires_in"]) # You don't normally need to care about this. - # It will be good for at least 5 minutes. -else: - print(result.get("error")) - print(result.get("error_description")) - print(result.get("correlation_id")) # You may need this when reporting a bug - From 62752cf933589aa06f72ec99f80df4b7e81d7f66 Mon Sep 17 00:00:00 2001 From: Abhidnya Patil Date: Tue, 23 Apr 2019 11:55:55 -0700 Subject: [PATCH 5/8] Adding comments --- .../authorization_code_flow_sample.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sample/authorization-code-flow-sample/authorization_code_flow_sample.py b/sample/authorization-code-flow-sample/authorization_code_flow_sample.py index e374064e..5ea33f85 100644 --- a/sample/authorization-code-flow-sample/authorization_code_flow_sample.py +++ b/sample/authorization-code-flow-sample/authorization_code_flow_sample.py @@ -4,7 +4,9 @@ { "authority": "https://login.microsoftonline.com/organizations", "client_id": "your_client_id", - "scope": ["https://graph.microsoft.com/.default"] + "scope": ["https://graph.microsoft.com/.default"], + "redirect_uri": "http://localhost:5000/getAToken", + // Configure this redirect uri for this sample "client_secret": "yoursecret" } @@ -60,6 +62,8 @@ def login(): return resp +# Our configured redirect uri is http://localhost:8000/getAToken. +# This is where it comes back after getting back auth code from AAD @app.route("/getAToken") def main_logic(): code = flask.request.args['code'] From c7de03fad10ca9ef706d99075c55b8b9e16fcb58 Mon Sep 17 00:00:00 2001 From: Abhidnya Patil Date: Tue, 23 Apr 2019 15:24:54 -0700 Subject: [PATCH 6/8] Addressing PR comments --- .../authorization_code_flow_sample.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/sample/authorization-code-flow-sample/authorization_code_flow_sample.py b/sample/authorization-code-flow-sample/authorization_code_flow_sample.py index 5ea33f85..fedcd251 100644 --- a/sample/authorization-code-flow-sample/authorization_code_flow_sample.py +++ b/sample/authorization-code-flow-sample/authorization_code_flow_sample.py @@ -71,11 +71,7 @@ def main_logic(): if state != flask.session['state']: raise ValueError("State does not match") - result = application.acquire_token_silent(config["scope"], account=None) - - if not result: - logging.info("No suitable token exists in cache. Let's get a new one from AAD.") - result = application.acquire_token_by_authorization_code(code, scopes=config["scope"], + result = application.acquire_token_by_authorization_code(code, scopes=config["scope"], redirect_uri=config['redirect_uri']) return flask.render_template('display.html', auth_result=result) From 4ab2d2b2d109bb583427afc2b36599acca7341bd Mon Sep 17 00:00:00 2001 From: Abhidnya Patil Date: Mon, 29 Apr 2019 16:57:37 -0700 Subject: [PATCH 7/8] Addressing PR comments --- .../authorization_code_flow_sample.py | 10 ++++------ .../templates/display.html | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/sample/authorization-code-flow-sample/authorization_code_flow_sample.py b/sample/authorization-code-flow-sample/authorization_code_flow_sample.py index fedcd251..5ae3895f 100644 --- a/sample/authorization-code-flow-sample/authorization_code_flow_sample.py +++ b/sample/authorization-code-flow-sample/authorization_code_flow_sample.py @@ -11,8 +11,9 @@ } You can then run this sample with a JSON configuration file: - python sample.py parameters.json + On the browser open http://localhost:5000/ + """ import sys # For simplicity, we'll read config file from 1st CLI param sys.argv[1] @@ -20,7 +21,7 @@ import logging import uuid -import flask as flask +import flask import msal @@ -45,9 +46,8 @@ @app.route("/") def main(): - login_url = 'http://localhost:5000/login' resp = flask.Response(status=307) - resp.headers['location'] = login_url + resp.headers['location'] = '/login' return resp @@ -62,8 +62,6 @@ def login(): return resp -# Our configured redirect uri is http://localhost:8000/getAToken. -# This is where it comes back after getting back auth code from AAD @app.route("/getAToken") def main_logic(): code = flask.request.args['code'] diff --git a/sample/authorization-code-flow-sample/templates/display.html b/sample/authorization-code-flow-sample/templates/display.html index a82e8bee..78522b70 100644 --- a/sample/authorization-code-flow-sample/templates/display.html +++ b/sample/authorization-code-flow-sample/templates/display.html @@ -5,7 +5,7 @@ Acquire Token Result -User Data: +Acquire Token Result
{{ key }} {{ value }}
{% for key, value in auth_result.items() %} From 9cb65629fe3196c13c1199fcf2f1f9d3af7340d0 Mon Sep 17 00:00:00 2001 From: Abhidnya Patil Date: Tue, 30 Apr 2019 10:47:21 -0700 Subject: [PATCH 8/8] Adding links to documentation --- .../authorization_code_flow_sample.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sample/authorization-code-flow-sample/authorization_code_flow_sample.py b/sample/authorization-code-flow-sample/authorization_code_flow_sample.py index 5ae3895f..43cc87c2 100644 --- a/sample/authorization-code-flow-sample/authorization_code_flow_sample.py +++ b/sample/authorization-code-flow-sample/authorization_code_flow_sample.py @@ -7,6 +7,8 @@ "scope": ["https://graph.microsoft.com/.default"], "redirect_uri": "http://localhost:5000/getAToken", // Configure this redirect uri for this sample + // redirect_uri should match what you've configured in here + // https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-configure-app-access-web-apis#add-redirect-uris-to-your-application "client_secret": "yoursecret" }