Using (LDAP) authorization in Pages

Hello everybody!

In our company all users are authenticated all the time and we access ldap database for authorization. Before pages (with custom rooting) I could access the user ticket using flask.request.headers but it had to be called in rooting callback. Then using ldap3.Connection I authorize the user and show or not show the requested page.

Using pages this rooting callback is not accessible so my question is if this is somehow possible with Pages or not? Should I create some separate callback or maybe some flask would help here? I am not familiar with flask although.

Thanks for the help, if you would need some additional information I can add it :slight_smile:

Hello @martin2097,

All requests are available at the time of page render, if using a layout.

You could also use flask before_request and determine if the page is something the user has access to or not.

2 Likes

Thanks @jinnyzor !

So if I understand you correctly, if my layout is a function I should be able to do authentification and authorization there?

Gonna give it a try thank you!

Correct, you’ll need to make sure there is a request first, otherwise you will run into issues with the verification.

1 Like

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: