Skip to content

Commit

Permalink
Convert most built-in samples from json to dotenv
Browse files Browse the repository at this point in the history
  • Loading branch information
rayluo committed Apr 3, 2024
1 parent 2d03ad9 commit 98bcfb3
Show file tree
Hide file tree
Showing 7 changed files with 221 additions and 120 deletions.
26 changes: 26 additions & 0 deletions sample/.env.sample.entra-id
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# This sample can be configured to work with Microsoft Entra ID.
#
# If you are using a Microsoft Entra ID tenant,
# configure the AUTHORITY variable as
# "https://login.microsoftonline.com/TENANT_GUID"
# or "https://login.microsoftonline.com/contoso.onmicrosoft.com".
#
# Alternatively, use "https://login.microsoftonline.com/common" for multi-tenant app.
AUTHORITY=<authority url>

# The following variables are required for the app to run.
CLIENT_ID=<client id>

# Leave it empty if you are using a public client which has no client secret.
CLIENT_SECRET=<client secret>

# Multiple scopes can be added into the same line, separated by a space.
# Here we use a Microsoft Graph API as an example
# You may need to use your own API's scope.
SCOPE=<your scopes>

# Required if the sample app wants to call an API.
#ENDPOINT=https://graph.microsoft.com/v1.0/me

# Required if you are testing the username_password_sample.py
#USERNAME=<username>
22 changes: 22 additions & 0 deletions sample/.env.sample.external-id
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# This sample can be configured to work with Microsoft External ID.
#
# If you are using a Microsoft Entra External ID for customers (CIAM) tenant,
# configure AUTHORITY as https://contoso.ciamlogin.com/contoso.onmicrosoft.com
AUTHORITY=<authority url>

# The following variables are required for the app to run.
CLIENT_ID=<client id>

# Leave it empty if you are using a public client which has no client secret.
CLIENT_SECRET=<client secret>

# Multiple scopes can be added into the same line, separated by a space.
# Here we use a Microsoft Graph API as an example
# You may need to use your own API's scope.
SCOPE=<your scopes>

# Required if the sample app wants to call an API.
#ENDPOINT=https://graph.microsoft.com/v1.0/me

# Required if you are testing the username_password_sample.py
#USERNAME=<username>
23 changes: 23 additions & 0 deletions sample/.env.sample.external-id-custom-domain
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# This sample can be configured to work with Microsoft External ID with custom domain.
#
# If you are using a Microsoft External ID tenant with custom domain,
# configure the OIDC_AUTHORITY variable as
# "https://www.contoso.com/TENANT_GUID/v2.0"
OIDC_AUTHORITY=<authority url>

# The following variables are required for the app to run.
CLIENT_ID=<client id>

# Leave it empty if you are using a public client which has no client secret.
CLIENT_SECRET=<client secret>

# Multiple scopes can be added into the same line, separated by a space.
# Here we use a Microsoft Graph API as an example
# You may need to use your own API's scope.
SCOPE=<your scopes>

# Required if the sample app wants to call an API.
#ENDPOINT=https://graph.microsoft.com/v1.0/me

# Required if you are testing the username_password_sample.py
#USERNAME=<username>
72 changes: 35 additions & 37 deletions sample/confidential_client_secret_sample.py
Original file line number Diff line number Diff line change
@@ -1,46 +1,37 @@
"""
The configuration file would look like this (sans those // comments):
{
"authority": "https://login.microsoftonline.com/Enter_the_Tenant_Name_Here",
"client_id": "your_client_id came from https://learn.microsoft.com/entra/identity-platform/quickstart-register-app",
"scope": ["https://graph.microsoft.com/.default"],
// Specific to Client Credentials Grant i.e. acquire_token_for_client(),
// you don't specify, in the code, the individual scopes you want to access.
// Instead, you statically declared them when registering your application.
// Therefore the only possible scope is "resource/.default"
// (here "https://graph.microsoft.com/.default")
// which means "the static permissions defined in the application".
"secret": "The secret generated by AAD during your confidential app registration",
// For information about generating client secret, refer:
// https://github.com/AzureAD/microsoft-authentication-library-for-python/wiki/Client-Credentials#registering-client-secrets-using-the-application-registration-portal
"endpoint": "https://graph.microsoft.com/v1.0/users"
// For this resource to work, you need to visit Application Permissions
// page in portal, declare scope User.Read.All, which needs admin consent
// https://github.com/Azure-Samples/ms-identity-python-daemon/blob/master/1-Call-MsGraph-WithSecret/README.md
}
You can then run this sample with a JSON configuration file:
python sample.py parameters.json
This sample demonstrates a daemon application that acquires a token using a
client secret and then calls a web API with the token.
This sample loads its configuration from a .env file.
To make this sample work, you need to choose one of the following templates:
.env.sample.entra-id
.env.sample.external-id
.env.sample.external-id-with-custom-domain
Copy the chosen template to a new file named .env, and fill in the values.
You can then run this sample:
python name_of_this_script.py
"""

import sys # For simplicity, we'll read config file from 1st CLI param sys.argv[1]
import json
import logging
import os
import time

import requests
from dotenv import load_dotenv # Need "pip install python-dotenv"
import msal
import requests


# Optional logging
# logging.basicConfig(level=logging.DEBUG) # Enable DEBUG log for entire script
# logging.getLogger("msal").setLevel(logging.INFO) # Optionally disable MSAL DEBUG logs

config = json.load(open(sys.argv[1]))
load_dotenv() # We use this to load configuration from a .env file

# If for whatever reason you plan to recreate same ClientApplication periodically,
# you shall create one global token cache and reuse it by each ClientApplication
Expand All @@ -49,25 +40,32 @@

# Create a preferably long-lived app instance, to avoid the overhead of app creation
global_app = msal.ConfidentialClientApplication(
config["client_id"], authority=config["authority"],
client_credential=config["secret"],
os.getenv('CLIENT_ID'),
authority=os.getenv('AUTHORITY'), # For Entra ID or External ID
oidc_authority=os.getenv('OIDC_AUTHORITY'), # For External ID with custom domain
client_credential=os.getenv('CLIENT_SECRET'),
token_cache=global_token_cache, # Let this app (re)use an existing token cache.
# If absent, ClientApplication will create its own empty token cache
)
scopes = os.getenv("SCOPE", "").split()


def acquire_and_use_token():
# Since MSAL 1.23, acquire_token_for_client(...) will automatically look up
# a token from cache, and fall back to acquire a fresh token when needed.
result = global_app.acquire_token_for_client(scopes=config["scope"])
result = global_app.acquire_token_for_client(scopes=scopes)

if "access_token" in result:
print("Token was obtained from:", result["token_source"]) # Since MSAL 1.25
# Calling graph using the access token
graph_data = requests.get( # Use token to call downstream service
config["endpoint"],
headers={'Authorization': 'Bearer ' + result['access_token']},).json()
print("Graph API call result: %s" % json.dumps(graph_data, indent=2))
if os.getenv('ENDPOINT'):
# Calling a web API using the access token
api_result = requests.get(
os.getenv('ENDPOINT'),
headers={'Authorization': 'Bearer ' + result['access_token']},
).json() # Assuming the response is JSON
print("Web API call result", json.dumps(api_result, indent=2))
else:
print("Token acquisition result", json.dumps(result, indent=2))
else:
print("Token acquisition failed", result) # Examine result["error_description"] etc. to diagnose error

Expand Down
56 changes: 33 additions & 23 deletions sample/device_flow_sample.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,39 @@
"""
This sample demonstrates a headless application that acquires a token using
the device code flow and then calls a web API with the token.
The configuration file would look like this:
{
"authority": "https://login.microsoftonline.com/common",
"client_id": "your_client_id came from https://learn.microsoft.com/entra/identity-platform/quickstart-register-app",
"scope": ["User.ReadBasic.All"],
// You can find the other permission names from this document
// https://docs.microsoft.com/en-us/graph/permissions-reference
"endpoint": "https://graph.microsoft.com/v1.0/users"
// You can find more Microsoft Graph API endpoints from Graph Explorer
// https://developer.microsoft.com/en-us/graph/graph-explorer
}
This sample loads its configuration from a .env file.
You can then run this sample with a JSON configuration file:
To make this sample work, you need to choose one of the following templates:
python sample.py parameters.json
.env.sample.entra-id
.env.sample.external-id
.env.sample.external-id-with-custom-domain
Copy the chosen template to a new file named .env, and fill in the values.
You can then run this sample:
python name_of_this_script.py
"""

import sys # For simplicity, we'll read config file from 1st CLI param sys.argv[1]
import json
import logging
import os
import sys
import time

import requests
from dotenv import load_dotenv # Need "pip install python-dotenv"
import msal
import requests


# Optional logging
# logging.basicConfig(level=logging.DEBUG) # Enable DEBUG log for entire script
# logging.getLogger("msal").setLevel(logging.INFO) # Optionally disable MSAL DEBUG logs

config = json.load(open(sys.argv[1]))
load_dotenv() # We use this to load configuration from a .env file

# If for whatever reason you plan to recreate same ClientApplication periodically,
# you shall create one global token cache and reuse it by each ClientApplication
Expand All @@ -39,10 +42,13 @@

# Create a preferably long-lived app instance, to avoid the overhead of app creation
global_app = msal.PublicClientApplication(
config["client_id"], authority=config["authority"],
os.getenv('CLIENT_ID'),
authority=os.getenv('AUTHORITY'), # For Entra ID or External ID
oidc_authority=os.getenv('OIDC_AUTHORITY'), # For External ID with custom domain
token_cache=global_token_cache, # Let this app (re)use an existing token cache.
# If absent, ClientApplication will create its own empty token cache
)
scopes = os.getenv("SCOPE", "").split()


def acquire_and_use_token():
Expand All @@ -61,12 +67,12 @@ def acquire_and_use_token():
# Assuming the end user chose this one
chosen = accounts[0]
# Now let's try to find a token in cache for this account
result = global_app.acquire_token_silent(config["scope"], account=chosen)
result = global_app.acquire_token_silent(scopes, account=chosen)

if not result:
logging.info("No suitable token exists in cache. Let's get a new one from AAD.")

flow = global_app.initiate_device_flow(scopes=config["scope"])
flow = global_app.initiate_device_flow(scopes=scopes)
if "user_code" not in flow:
raise ValueError(
"Fail to create device flow. Err: %s" % json.dumps(flow, indent=4))
Expand All @@ -85,11 +91,15 @@ def acquire_and_use_token():

if "access_token" in result:
print("Token was obtained from:", result["token_source"]) # Since MSAL 1.25
# Calling graph using the access token
graph_data = requests.get( # Use token to call downstream service
config["endpoint"],
headers={'Authorization': 'Bearer ' + result['access_token']},).json()
print("Graph API call result: %s" % json.dumps(graph_data, indent=2))
if os.getenv('ENDPOINT'):
# Calling a web API using the access token
api_result = requests.get(
os.getenv('ENDPOINT'),
headers={'Authorization': 'Bearer ' + result['access_token']},
).json() # Assuming the response is JSON
print("Web API call result", json.dumps(api_result, indent=2))
else:
print("Token acquisition result", json.dumps(result, indent=2))
else:
print("Token acquisition failed", result) # Examine result["error_description"] etc. to diagnose error

Expand Down
Loading

0 comments on commit 98bcfb3

Please sign in to comment.