How to secure a multi-page Dash app with dash-auth

In this tutorial, we will see how to secure your Dash multi-page app using the basic authentication mechanism in dash-auth.

It’s the natural continuation of the previous tutorial: How to secure a Dash app with dash-auth, where you can learn how to use dash-auth, connect to a database, and display the username.

Let’s go!

Dash-auth

If you haven’t yet, install dash and dash-auth:

pip install dash dash-auth

Then, create three files and one folder:

  • app.py: the main app file
  • pages/public.py: the publicly accessible page at URL “/”
  • pages/private.py: the private page at URL “/private”

app.py

This file will be very similar to what we had in the previous tutorial. The list of users is hardcoded at the beginning of the file:

from dash import Dash, html, dcc, page_container
import dash_auth

# Note: better store the list in a secrets file or in a database.
USERS = {
    "alice": "secret123",
    "bob": "pa$$w0rd"
}

# Create the Dash app
app = Dash(
    __name__,
    use_pages=True,
    suppress_callback_exceptions=True
)

# Add authentication
auth = dash_auth.BasicAuth(
    app,
    username_password_list=USERS,
    secret_key="something_like_nUGz8DZvb...",
    # Public routes are not protected by authentication.
    # All others are protected by default
    public_routes=["/"]
)

# Define the layout
app.layout = html.Div([
    html.H1("My app"),
    html.P(dcc.Link("Public page", href="/")),
    html.P(dcc.Link("Private page", href="/private")),
    html.Hr(),
    page_container
])

if __name__ == '__main__':
    app.run(debug=True)

There are two key differences:

  • We use pages for the Dash app (use_pages=True and suppress_callback_exceptions=True)
  • We define the public pages in Dash-auth using public_routes.

Then, the layout shows two links to the two pages and the page content (using page_container).

pages/public.py

The public page will contain a button that increments itself when clicked:

from dash import html, dcc, register_page, callback, Output, Input
from dash_auth import public_callback

# The public page is the home
register_page(
    __name__,
    "/",
)

# The page layout
def layout():
    return html.Div([
        html.P("This is a public page."),
        html.Button("Click me (0)", id="button", n_clicks=0)
    ])

# A simple public callback
@public_callback(
    Output("button", "children"),
    Input("button", "n_clicks"),
)
def update_button(n_clicks):
    return f"Click me ({n_clicks})"

The only key change here is the use of public_callback (from dash-auth) instead of callback.

The public, unprotected page

By default, all callbacks use the same URL endpoint: /dash-update-component. This is why we need to specify that this callback is public, while others are protected by default as soon as we add dash-auth to the app.

Under the hood, the callback ID is simply added to a list of whitelisted callbacks in the Flask server’s config.

pages/private.py

The private page will only show the username using a layout function:

from dash import html, register_page
import flask

register_page(
    __name__,
    "/private",
)

def layout():
    username = flask.session.get("user").get("email")

    return html.Div([
        html.P("If you see this, you are authenticated ✅"),
        html.P(f"You are logged in as {username}.")
    ])
The private page, protected by HTTP basic authentification.

dash-auth saves the current username in a flask cookie, so we can get easily get it in the layout function, or within a callback.

Result

Now let’s run this app!
Here is a little video of what it looks like:

⚠️ Note that once we’re logged in, we can’t log out manually. The browser must be closed instead.

Conclusion

I hope this tutorial was instructive! I found it interesting that dash-auth evolved to secure multi-page apps as well, which I didn’t know until writing these two tutorials 🙂

As with the previous tutorial, the conclusion is similar: dash-auth has its drawbacks, but it’s a fairly easy solution to add an authentication layer to a dashboard or a data app. However, I would not recommend using it for production apps that have very sensitive data.

If you need proper login/logout behavior (e.g., session expiration, multiple users switching), you’ll need a more advanced system such as:

  • flask_login
  • OAuth (Google, GitHub…)
  • Enterprise identity providers (Okta, Auth0, Azure AD…)
  • Dash-Enterprise (handles LDAP, SAML, OIDC)

Happy Dash coding!