Thank you @jinnyzor for you help! I also found an information about Flask session in another topic created by you:
Dash App Pages with Flask Login Flow using Flask - Dash Python - Plotly Community Forum
Based on all of these information I created the authentication and authorization system capable to authorize each page with different role. This is not a full code - just a partial code showing how authentication and authorization could be implemented.
Since I do not want to check for roles on authorization server every time the user changes the page I check for all user roles when the app is accessed first time. Then I save all user roles to Flask session. From my understanding the session is basically a secured dcc.Store. dcc.Store can be accessed and changed from developer console but session is encrypted.
app.py
:
import dash
from dash import Dash, html, page_container
import dash_mantine_components as dmc
import auth
from flask import Flask, session
import os
server = Flask(__name__) # you have to expose Flask server to access session
server.secret_key = os.urandom(32) # secret key to encrypt the session cookie
app = Dash(__name__, server=server, use_pages=True)
def unique_list(lst):
# initialize a null list
unique_lst = []
# traverse for all elements
for x in lst:
# check if exists in unique_list or not
if x not in unique_lst:
unique_lst.append(x)
return unique_lst
# links can be autogenerated from page_registry
links = {
"/": {"label": "Home"},
"auth-page": {"label": "Auth Page"},
}
def serve_layout():
ticket = auth.get_ticket() # authentication - returns ID of user
# all unique role - app combinations used in pages which we have to check access to
role_app_list = unique_list(
[
[role, app]
for role, app in zip(
[page["auth_role"] for page in dash.page_registry.values()],
[page["auth_app"] for page in dash.page_registry.values()],
)
]
)
valid_roles = []
for role, app in role_app_list:
if auth.validate_ticket(ticket, role, app): # check if user have the role assigned
valid_roles.append([role, app])
session["roles"] = valid_roles # safely cache all roles user has access to
return html.Div(
[
html.Div(
[
dmc.NavLink(
label=links[link]["label"],
href=link,
)
for link in links
]
),
page_container,
]
)
app.layout = serve_layout
if __name__ == "__main__":
app.run(debug=True)
home.py
:
import dash
from dash import register_page
import dash_mantine_components as dmc
from flask import session
# add custom key-value pairs to specify app - role combination needed to access the page
register_page(__name__, path="/", auth_role="lower_privilages", auth_app="my_app")
def layout():
# from session cookie check if user has the correct role assigned
if [
dash.page_registry[__name__]["auth_role"],
dash.page_registry[__name__]["auth_app"],
] in session["roles"]:
return dmc.Text("I am home") # if yes show the content
else:
return dmc.Text("Not Authorized") # if not do not show the content
auth-page.py
:
import dash
from dash import register_page
import dash_mantine_components as dmc
from flask import session
# add custom key-value pairs to specify app - role combination needed to access the page
register_page(__name__, auth_role="higher_privilages", auth_app="my_app")
def layout():
# from session cookie check if user has the correct role assigned
if [
dash.page_registry[__name__]["auth_role"],
dash.page_registry[__name__]["auth_app"],
] in session["roles"]:
return dmc.Text("I am authenticated page") # if yes show the content
else:
return dmc.Text("Not Authorized") # if not do not show the content
Any feedback is welcomed, hopefully this will help somebody in a future