Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Style fixes and Python best practices #91

Merged
merged 19 commits into from
Mar 9, 2023
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
FLASK_DEBUG=True
CLIENT_ID=<client id>
CLIENT_SECRET=<client secret>
TENANT_ID=<tenant id>
rayluo marked this conversation as resolved.
Show resolved Hide resolved
41 changes: 27 additions & 14 deletions app.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from flask import Flask, render_template, session, request, redirect, url_for
from flask_session import Session # https://pythonhosted.org/Flask-Session
import identity, identity.web
import identity
import identity.web
import requests
import app_config
from flask import Flask, redirect, render_template, request, session, url_for
from flask_session import Session

import app_config
rayluo marked this conversation as resolved.
Show resolved Hide resolved

app = Flask(__name__)
app.config.from_object(app_config)
Expand All @@ -12,7 +13,7 @@
# This section is needed for url_for("foo", _external=True) to automatically
# generate http scheme when this sample is running on localhost,
# and to generate https scheme when it is deployed behind reversed proxy.
# See also https://flask.palletsprojects.com/en/1.0.x/deploying/wsgi-standalone/#proxy-setups
# See also https://flask.palletsprojects.com/en/2.2.x/deploying/proxy_fix/
from werkzeug.middleware.proxy_fix import ProxyFix
app.wsgi_app = ProxyFix(app.wsgi_app, x_proto=1, x_host=1)

Expand All @@ -21,41 +22,53 @@
authority=app.config.get("AUTHORITY"),
client_id=app.config["CLIENT_ID"],
client_credential=app.config["CLIENT_SECRET"],
)
)
rayluo marked this conversation as resolved.
Show resolved Hide resolved


@app.route("/login")
def login():
return render_template("login.html", version=identity.__version__, **auth.log_in(
scopes=app_config.SCOPE, # Have user consent scopes during log-in
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
))
result = auth.log_in(
rayluo marked this conversation as resolved.
Show resolved Hide resolved
# Have user consent scopes during log-in
scopes=app_config.SCOPE,
# Optional. If present, this absolute URL must match your app's redirect_uri registered in Azure Portal
redirect_uri=url_for("auth_response", _external=True),
)
rayluo marked this conversation as resolved.
Show resolved Hide resolved
return render_template("login.html", version=identity.__version__, **result)


@app.route(app_config.REDIRECT_PATH)
def auth_response():
result = auth.complete_log_in(request.args)
return render_template("auth_error.html", result=result) if "error" in result else redirect(url_for("index"))
if "error" not in result:
rayluo marked this conversation as resolved.
Show resolved Hide resolved
return redirect(url_for("index"))
return render_template("auth_error.html", result=result)


@app.route("/logout")
def logout():
return redirect(auth.log_out(url_for("index", _external=True)))


@app.route("/")
def index():
if not auth.get_user():
return redirect(url_for("login"))
return render_template('index.html', user=auth.get_user(), version=identity.__version__)


@app.route("/call_downstream_api")
def call_downstream_api():
token = auth.get_token_for_user(app_config.SCOPE)
if "error" in token:
return redirect(url_for("login"))
api_result = requests.get( # Use token to call downstream api
# Use access token to call downstream api
api_result = requests.get(
app_config.ENDPOINT,
headers={'Authorization': 'Bearer ' + token['access_token']},
).json()
timeout=30
rayluo marked this conversation as resolved.
Show resolved Hide resolved
).json()
return render_template('display.html', result=api_result)


if __name__ == "__main__":
app.run()

26 changes: 12 additions & 14 deletions app_config.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,16 @@
import os

CLIENT_ID = "Enter_the_Application_Id_here" # Application (client) ID of app registration
# Application (client) ID of app registration
CLIENT_ID = os.getenv("CLIENT_ID")
rayluo marked this conversation as resolved.
Show resolved Hide resolved
# Application's generated client secret: never check this into source control!
CLIENT_SECRET = os.getenv("CLIENT_SECRET")

CLIENT_SECRET = "Enter_the_Client_Secret_Here" # Placeholder - for use ONLY during testing.
# In a production app, we recommend you use a more secure method of storing your secret,
# like Azure Key Vault. Or, use an environment variable as described in Flask's documentation:
# https://flask.palletsprojects.com/en/1.1.x/config/#configuring-from-environment-variables
# CLIENT_SECRET = os.getenv("CLIENT_SECRET")
# if not CLIENT_SECRET:
# raise ValueError("Need to define CLIENT_SECRET environment variable")

AUTHORITY = "https://login.microsoftonline.com/common" # For multi-tenant app
# AUTHORITY = "https://login.microsoftonline.com/Enter_the_Tenant_Name_Here"
#AUTHORITY = "https://login.microsoftonline.com/" # For multi-tenant app
AUTHORITY = f"https://login.microsoftonline.com/{os.getenv('TENANT_ID')}"

REDIRECT_PATH = "/getAToken" # Used for forming an absolute URL to your redirect URI.
# The absolute URL must match the redirect URI you set
# in the app's registration in the Azure portal.
# The absolute URL must match the redirect URI you set
# in the app's registration in the Azure portal.

# You can find more Microsoft Graph API endpoints from Graph Explorer
# https://developer.microsoft.com/en-us/graph/graph-explorer
Expand All @@ -25,4 +20,7 @@
# https://docs.microsoft.com/en-us/graph/permissions-reference
SCOPE = ["User.ReadBasic.All"]

SESSION_TYPE = "filesystem" # Specifies the token cache should be stored in server-side session
# Tells the Flask-session2 extension to store sessions in the filesystem
SESSION_TYPE = "filesystem"
# Using the file system will not work in most production systems,
# it's better to use a database-backed session store instead.
40 changes: 16 additions & 24 deletions app_config_b2c.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,31 @@
editprofile_user_flow = "B2C_1_profileediting1"

resetpassword_user_flow = "B2C_1_passwordreset1" # Note: Legacy setting.
# If you are using the new
# "Recommended user flow" (https://docs.microsoft.com/en-us/azure/active-directory-b2c/user-flow-versions),
# you can remove the resetpassword_user_flow and the B2C_RESET_PASSWORD_AUTHORITY settings from this file.
# If you are using the new
# "Recommended user flow" (https://docs.microsoft.com/en-us/azure/active-directory-b2c/user-flow-versions),
# you can remove the resetpassword_user_flow and the B2C_RESET_PASSWORD_AUTHORITY settings from this file.

authority_template = "https://{tenant}.b2clogin.com/{tenant}.onmicrosoft.com/{user_flow}"
rayluo marked this conversation as resolved.
Show resolved Hide resolved

CLIENT_ID = "Enter_the_Application_Id_here" # Application (client) ID of app registration
# Application (client) ID of app registration
CLIENT_ID = os.getenv("CLIENT_ID")
# Application's generated client secret: never check this into source control!
CLIENT_SECRET = os.getenv("CLIENT_SECRET")

CLIENT_SECRET = "Enter_the_Client_Secret_Here" # Placeholder - for use ONLY during testing.
# In a production app, we recommend you use a more secure method of storing your secret,
# like Azure Key Vault. Or, use an environment variable as described in Flask's documentation:
# https://flask.palletsprojects.com/en/1.1.x/config/#configuring-from-environment-variables
# CLIENT_SECRET = os.getenv("CLIENT_SECRET")
# if not CLIENT_SECRET:
# raise ValueError("Need to define CLIENT_SECRET environment variable")
AUTHORITY = authority_template.format(tenant=b2c_tenant, user_flow=signupsignin_user_flow)
B2C_PROFILE_AUTHORITY = authority_template.format(tenant=b2c_tenant, user_flow=editprofile_user_flow)

AUTHORITY = authority_template.format(
tenant=b2c_tenant, user_flow=signupsignin_user_flow)
B2C_PROFILE_AUTHORITY = authority_template.format(
tenant=b2c_tenant, user_flow=editprofile_user_flow)

B2C_RESET_PASSWORD_AUTHORITY = authority_template.format(
tenant=b2c_tenant, user_flow=resetpassword_user_flow)
# If you are using the new
# "Recommended user flow" (https://docs.microsoft.com/en-us/azure/active-directory-b2c/user-flow-versions),
# you can remove the resetpassword_user_flow and the B2C_RESET_PASSWORD_AUTHORITY settings from this file.
B2C_RESET_PASSWORD_AUTHORITY = authority_template.format(tenant=b2c_tenant, user_flow=resetpassword_user_flow)
# If you are using the new
# "Recommended user flow" (https://docs.microsoft.com/en-us/azure/active-directory-b2c/user-flow-versions),
# you can remove the resetpassword_user_flow and the B2C_RESET_PASSWORD_AUTHORITY settings from this file.

REDIRECT_PATH = "/getAToken" # Used for forming an absolute URL to your redirect URI.
# The absolute URL must match the redirect URI you set
# in the app's registration in the Azure portal.
# The absolute URL must match the redirect URI you set
# in the app's registration in the Azure portal.

# This is the API resource endpoint
ENDPOINT = '' # Application ID URI of app registration in Azure portal
ENDPOINT = '' # Application ID URI of app registration in Azure portal

# These are the scopes you've exposed in the web API app registration in the Azure portal
SCOPE = [] # Example with two exposed scopes: ["demo.read", "demo.write"]
Expand Down
22 changes: 2 additions & 20 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,24 +1,6 @@
## The following combination still work, but later versions of 1.x were broken
# due to their upstream package brought in a breaking change
# https://github.com/pallets/markupsafe/issues/286
# Flask==1.1.4
# werkzeug==1.0.1
# Jinja2==2.11.3
# MarkupSafe==2.0.1
rayluo marked this conversation as resolved.
Show resolved Hide resolved
#
# Since Flask family do not use semantic versioning, we remove our upper bound here
Flask>=2
Flask-Session2
rayluo marked this conversation as resolved.
Show resolved Hide resolved
werkzeug>=2

flask-session>=0.3.2,<0.5
requests>=2,<3
identity>=0.2,<0.3

# cachelib==0.1 # Only need this if you are running Python 2
rayluo marked this conversation as resolved.
Show resolved Hide resolved
# Note: This sample does NOT directly depend on cachelib.
# It is an indirect dependency of flask-session.
# Cachelib 0.1.1 no longer supports Python 2
# (see also https://github.com/pallets/cachelib/issues/14)
# So, if you still need to run your app in Python 2,
# your workaround is to pin cachelib to its older version 0.1,
# but keep in mind it contains a known bug https://github.com/pallets/cachelib/pull/12
python-dotenv
rayluo marked this conversation as resolved.
Show resolved Hide resolved