Dash App Pages with Flask Login Flow using Flask

Hello @JordyS,

Sure thing.

For this, query the users db into a dataframe, set the index as the username and then convert to a dictionary.

If it isn’t this, it should be close.

Edit:
You will probably need to change the password lookup to pull from the password key as well.

Another thing to note with sql, you should never take user inputs directly into a sql string. That can lead to sql injection attacks.

1 Like

Hi,

With your example, how to add a reset_password page in dash, and the reset-button may hold in the login page or hold in the page that mentioned the username/password is invalid?

Hi @jinnyzor ,

With your example, how to add a reset_password page in dash, and the reset-button may hold in the login page or hold in the page when the username/password is invalid

Hello @beginof,

What exactly do you mean by hold in the login page? You mean, not navigate away?

Which iteration are you using? A Dash layout for the login screen, or the flask template for the login screen?

Hi @jinnyzor,

I’m using the login page as same as your example.

In current version, the login page, it will have username-input-box, follow by the password-input-box and a clickable login-button. When user click the login-button, it will redirect to the home page if username and password matched.

In the next version, can add one more clickable button called reset-button ? which will perform to reset / change the password and able to update the new password to database. When user click the reset-button, it will redirect to the reset page, which contain username-input-box, current-password-input-box, new-password-input-box, confirm-password-input-box and a clickable submit-button. Once user click the submit-button, the new password will replace and update in the database, and redirect to the login page.

1 Like

HI @jinnyzor,

May I know how to create a change password page which able to perform change password and update the new password to database.

current password > new password > confirm password > submit btn

these would be great adds on the template. I would also suggest the a “Join” button for new users. I tried to include it as modal in the login.py (with username, password and confirm password) but it does not trigger the @callback, not sure where is best to include it

Hi , nice article,
May i ask, is it possible that will work with sign in users of only the active directory,
I want only domain users to sign in

This would only allow for users to sign in who are in the list.

But we can pick up using flask-dance in your other topic.

@jinnyzor
I need some help with adding login page to dash app and only show the page links in the Navigation bar based on the user role. I tried to go through the solution you provided above but couldn’t figure out how it can be implemented for my requirement.

My Folder structure is
-pages
–page-1
–page-2
-app.py
I’ve two roles admin and user. Both page links should be displayed in the navigation bar to user with admin role and page-1 link should only be displayed in navigation bar to user with user role.

My app.py file is below

import dash
from dash import html, dcc
import dash_bootstrap_components as dbc

dash_app = dash.Dash(
    __name__,
    use_pages=True,
    suppress_callback_exceptions=True,
    external_stylesheets=[dbc.themes.SPACELAB],
)
app = dash_app.server
navbar = dbc.Navbar(
    dbc.Container(
        [
            dbc.Row(
                [
                    dbc.Col(
                        [dbc.NavbarBrand("Dashboard", className="ms-2")],
                        width={"size": "auto"},
                    ),
                ],
                align="center",
                className="g-0",
            ),
            dbc.Row(
                [
                    dbc.Col(
                        [
                            dbc.Nav(
                                [
                                    dbc.NavItem(
                                        dbc.NavLink(
                                            [
                                                html.Div(
                                                    page["name"], className="ms-2"
                                                ),
                                            ],
                                            href=page["path"],
                                            active="exact",
                                        ),
                                    )
                                    for page in dash.page_registry.values()
                                ],
                                navbar=True,
                                horizontal=True,
                            )
                        ],
                        width={"size": "auto"},
                    )
                ],
                align="center",
                className="g-0",
            ),
        ],
        fluid=True,
    ),
    color="#041E42",
    dark=True,
)

dash_app.layout = dbc.Container(
    [
        dcc.Interval(id="timer", interval=1000 * 1800, n_intervals=0),
        dcc.Store(id="store-data", data=[], storage_type="session"),
        dbc.Row(
            [
                dbc.Col([navbar], xs=8, sm=8, md=12, lg=12, xl=12, xxl=12),
            ]
        ),
        html.Hr(),
        dbc.Row(
            [dbc.Col([dash.page_container], xs=8, sm=8, md=12, lg=12, xl=12, xxl=12)]
        ),
    ],
    fluid=True,
)


if __name__ == "__main__":
    dash_app.run_server(debug=True)

Appreciate your help

Hello @naveenatla,

To alter navbar links, you need to make the layout a function that will query the user’s level of permission.

Something like this:

import dash
from dash import html, dcc
import dash_bootstrap_components as dbc
import flask
from flask_login import current_user

dash_app = dash.Dash(
    __name__,
    use_pages=True,
    suppress_callback_exceptions=True,
    external_stylesheets=[dbc.themes.SPACELAB],
)
app = dash_app.server

users = {'user1': {'perm_level': 0}, 'user2': {'perm_level': 1}}

def navbar():
    links = []
    for page in dash.page_registry.values():
        if users[current_user]['perm_level'] >= page['perm_level'] or page.get('perm_level') is None:
            links.append(
                dbc.NavItem(
                    dbc.NavLink(
                        [
                            html.Div(
                                page["name"], className="ms-2"
                            ),
                        ],
                        href=page["path"],
                        active="exact",
                    ),
                )
            )
    
    navbar = dbc.Navbar(
        dbc.Container(
            [
                dbc.Row(
                    [
                        dbc.Col(
                            [dbc.NavbarBrand("Dashboard", className="ms-2")],
                            width={"size": "auto"},
                        ),
                    ],
                    align="center",
                    className="g-0",
                ),
                dbc.Row(
                    [
                        dbc.Col(
                            [
                                dbc.Nav(
                                    links,
                                    navbar=True,
                                    horizontal=True,
                                )
                            ],
                            width={"size": "auto"},
                        )
                    ],
                    align="center",
                    className="g-0",
                ),
            ],
            fluid=True,
        ),
        color="#041E42",
        dark=True,
    )

def layout():
    if not current_user:
        return html.Div('please log in')
    return dbc.Container(
        [
            dcc.Interval(id="timer", interval=1000 * 1800, n_intervals=0),
            dcc.Store(id="store-data", data=[], storage_type="session"),
            dbc.Row(
                [
                    dbc.Col([navbar()], xs=8, sm=8, md=12, lg=12, xl=12, xxl=12),
                ]
            ),
            html.Hr(),
            dbc.Row(
                [dbc.Col([dash.page_container], xs=8, sm=8, md=12, lg=12, xl=12, xxl=12)]
            ),
        ],
        fluid=True,
    )

dash_app.layout = layout


if __name__ == "__main__":
    dash_app.run_server(debug=True)

Then when you register your pages, add the perm_level argument.

1 Like

@jinnyzor
Thank You very much for the reply. I’ll try it and let you know the result.

1 Like

@jinnyzor This is a huge help, thank you! It’s great to have a flask-login implementation that can take place outside of Dash.

Under which license can we use the code that you posted on 2023-02-03? For instance, would we be able to implement it in commercial projects (while providing attribution)?

Hello @KBurchfiel,

Thank you for checking, this is free to use, no license.

This was just a very basic example of how to handle the login outside of Dash. I find it easier to manage this then trying to figure out what should be available before the login.

1 Like

Hello @jinnyzor,

Thank you so much! I really appreciate it.

I have updated a demo education dashboard I’m working on so that it implements your flask-login solution. The dashboard is very much a work in progress, but I thought it would be helpful to share so that users can see what your code looks like in practice. The interactive dashboard can be found at this link, and the project’s GitHub page can be found here.

This project is released under the MIT license, so users are welcome to use it as a basis for their own dashboards. It may be particularly useful for users who would like to host their dashboards via Google Cloud Run (which I’ve switched to now that Heroku’s free tier is gone :cry:). (The project’s program notes file is a bit disorganized at the moment but has some more information on the Cloud Run upload process.)

3 Likes

Nice!

You went with the template outside of Dash. I do feel that this is easier to manage than having one in Dash… less holes you have to punch through.

I would note that you should punch a hole through it for static, in case you want a picture or something.

1 Like

@jinnyzor I signed up for a Plotly account just to say: Thanks so much for this detailed guide. Amazing - thanks!! I added a route for /static/image.png; now I have a login page with image and everything else requires login and has the “current_user” available for queries.

1 Like

Hello @saysma,

Welcome to the community!

So glad that this was helpful to you. :grin:

With securing any site, I would always recommend testing and make sure the site is functioning properly and in an expected fashion. Especially if you use things like level based permissions. (User, admin, etc)

1 Like

Hi,

though I finally ended up with using the login flow outside of the dash app, I like to make all of you who prefer the route within Dash aware that you will need to extend the following if-statement:

if request.headers['Referer'] in ['http://127.0.0.1:8050/login', 'http://127.0.0.1:8050/logout'] and \
                        request.path in ['/_dash-layout', '/_dash-dependencies'] :

to:

if request.headers['Referer'] in ['http://127.0.0.1:8050/login', 'http://127.0.0.1:8050/logout'] and \
                        (request.path in ['/_dash-layout', '/_dash-dependencies'] or request.path.startswith('/assets/')):

to allow access to the css-files within the assets-folder.

@jinnyzor I would appreciate if you can comment whther this may introduce security issues. … and of course Thank You for sharing all your work!

1 Like

Hello @ivo,

Yeah, this looks like it will work. I dont believe that there are any security issues with asset folders, unless you have something specific that you dont want to share unless logged in. :slight_smile:

I’m not a security expert, but I dont see anything glaring out about this. XD

1 Like