'Share session data between Flask apps in separate Docker containers that are served using a reverse proxy
I have a Docker app running on localhost. There are multiple flask apps each in its own container. One is on the root (localhost) and the others are on subdomains (app.localhost). I use a traefik reverse proxy to serve the containers.
Without any authentication, the whole Docker app works great.
Then I try to authenticate the users. I am using Microsoft Authentication Library(Azure AD) for this. On the root app (localhost) this works great. If I am not logged in, it redirects me to a login page with a link. I click the link and now I am authorized. I am also able to pull the username from the http header.
However, when I go to a subdomain (app.localhost), it forgets I am logged in and then crashes because I try to run the same code of pulling the username from the http header, but it is missing.
Code for root app:
app = Flask(__name__)
app.config.from_object(app_config)
Session(app)
# login functions ######################################################################################
def _load_cache():
cache = msal.SerializableTokenCache()
if session.get("token_cache"):
cache.deserialize(session["token_cache"])
return cache
def _save_cache(cache):
if cache.has_state_changed:
session["token_cache"] = cache.serialize()
def _build_msal_app(cache=None, authority=None):
return msal.ConfidentialClientApplication(
app_config.CLIENT_ID, authority=authority or app_config.AUTHORITY,
client_credential=app_config.CLIENT_SECRET, token_cache=cache)
def _get_token_from_cache(scope=None):
cache = _load_cache() # This web app maintains one cache per session
cca = _build_msal_app(cache)
accounts = cca.get_accounts()
if accounts: # So all accounts belong to the current signed-in user
result = cca.acquire_token_silent(scope, account=accounts[0])
_save_cache(cache)
return result
@app.route('/login/')
def login():
session["state"] = str(uuid.uuid4())
auth_url = _build_msal_app().get_authorization_request_url(
app_config.SCOPE,
state=session["state"],
redirect_uri=url_for("authorized", _external=True))
return render_template('login.html', auth_url=auth_url)
@app.route("/auth") # This absolute URL must match your app's redirect_uri set in AAD
def authorized():
if request.args['state'] != session.get("state"):
return redirect(url_for("login"))
cache = _load_cache()
result = _build_msal_app(cache).acquire_token_by_authorization_code(
request.args['code'],
scopes=app_config.SCOPE,
redirect_uri=url_for("authorized", _external=True))
if "error" in result:
return "Login failure: %s, %s" % (
result["error"], result.get("error_description"))
session["user"] = result.get("id_token_claims")
_save_cache(cache)
return redirect(url_for("index"))
def get_token(scope):
token = _get_token_from_cache(scope)
if not token:
return redirect(url_for("login"))
return token
@app.route("/logout")
def logout():
session.clear() # Wipe out the user and the token cache from the session
return redirect( # Also need to log out from the Microsoft Identity platform
"https://login.microsoftonline.com/common/oauth2/v2.0/logout"
"?post_logout_redirect_uri=" + url_for("index", _external=True))
# actual app ##########################################################################################
@app.route('/')
def index():
# send to login page if user is not logged in
if not session.get('user'):
return redirect(url_for('login'))
else:
return render_template('index.html')
app_config.py
from os import environ
CLIENT_SECRET = environ["CLIENT_SECRET"]
AUTHORITY = environ["AUTHORITY"]
CLIENT_ID = environ["CLIENT_ID"]
SCOPE = ["User.ReadBasic.All"]
SESSION_TYPE = "filesystem"
A copy of this app_config file is in the directories for each flask app.
I tried adding this to the app_config files, but apparently localhost doesn't work as the cookie domain.
SESSION_COOKIE_NAME='localhost'
SESSION_COOKIE_DOMAIN='localhost'
REMEMBER_COOKIE_DOMAIN='localhost'
Then I read somewhere that dev.localhost could work. So I changed the Docker app to run on dev.localhost instead of localhost and added this to the app_config.
SESSION_COOKIE_NAME='dev.localhost'
SESSION_COOKIE_DOMAIN='dev.localhost'
REMEMBER_COOKIE_DOMAIN='dev.localhost'
This seemed liked it may work, but Microsoft doesn't allow dev.localhost/auth to be a redirect uri.
What do I need to do for the session to carry between subdomains/other flask apps?
Unfortunately I have to use Windows containers on a Windows Server 2019. I know they are not the best, but it is what I have to work with.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|