Using (LDAP) authorization in Pages

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 :slight_smile: