Integrating flask_login to dash app (and separating it into its own module)

I am attempting to integrate the flask-authentication into my app, as @AnnMarieW demonstrated here

I am trying to move all authentication lines of code into their own module including the callback.

However, when I run this, I get the following error:

An object was provided as `children` instead of a component, string, or number (or list of those). Check the children property that looks something like:
{
  "0": {
    "props": {
      "pathname": "/",
      "href": "http://127.0.0.1:8050/"
    }
  }
}

I have been at this for several hours now and still cannot figure out how to make this work, or if it is even possible.

Any help is appreciated. thank you!

****************app.py :

from flask import Flask
import dash
import src.page_container as page_container
import src.pages.login_callback as login_callback

server = Flask(__name__)
app = dash.Dash(
    __name__,
    server=server,
    use_pages=True,
    suppress_callback_exceptions=True,
    pages_folder='src'
)

login_callback.function1(app, server)

page_container.main_layout(app)

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

****************login_callback.py :

from flask import Flask
from flask_login import login_user, LoginManager, UserMixin, current_user
import dash
from dash import dcc, Input, Output, State

VALID_USERNAME_PASSWORD = {"test": "test", "hello": "world"}



def function1(app, server):
    # Updating the Flask Server configuration with Secret Key to encrypt the user session cookie
    server.config.update(SECRET_KEY='abcd1234')

    # Login manager object will be used to login / logout users
    login_manager = LoginManager()
    login_manager.init_app(server)
    login_manager.login_view = "/login"



    class User(UserMixin):
        # User data model. It has to have at least self.id as a minimum
        def __init__(self, username):
            self.id = username



    @login_manager.user_loader
    def load_user(username):
        return User(username)



    @app.callback(
        Output("user-status-header", "children"),
        Input("url", "pathname"),
    )
    def update_authentication_status(_):
        if current_user.is_authenticated:
            return dcc.Link("logout", href="/logout")
        return dcc.Link("login", href="/login")



    @app.callback(
        Output("output-state", "children"),
        Input("login-button", "n_clicks"),
        State("username", "value"),
        State("password", "value"),
        prevent_initial_call=True,
    )
    def login_button_click(n_clicks, username, password):
        if n_clicks > 0:
            if VALID_USERNAME_PASSWORD.get(username) is None:
                return "Invalid username"
            if VALID_USERNAME_PASSWORD.get(username) == password:
                login_user(User(username))
                return "Login Successful"
            return "Incorrect  password"

****************home.py :

import dash
from dash import html, dcc
from flask_login import current_user

dash.register_page(__name__, path="/")


def layout():
    if not current_user.is_authenticated:
        return html.Div(["Please ", dcc.Link("login", href="/login"), " to continue"])
    return html.Div([dcc.Link("Go to Page 1", href="/page-1")])

****************login.py :

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

dash.register_page(__name__, path="/login")

text_input_style = {
    'padding': '5px',
    'margin':'auto',
    'margin-top':'5px',
    'margin-bottom':'5px',
    #'border-color': '#878787',
    #'border-radius': 5,
    'width':'400px'
}

layout = dbc.Container([
    dbc.Row([
        dbc.Col([
            dmc.Group(direction='column', spacing='xs', position='center', children=[
                dbc.Input(
                    id='username',
                    placeholder='username',
                    style=text_input_style
                ),
                dbc.Input(
                    id='password',
                    placeholder='password',
                    style=text_input_style
                ),
                html.Button(children="Login", n_clicks=0, type="submit", id="login-button"),
                html.Div(children="", id="output-state"),
                html.Br(),
                dcc.Link("Home", href="/")
            ])
        ])
    ])
], fluid=True
)

****************logout.py :

import dash
from dash import html, dcc
from flask_login import logout_user, current_user

dash.register_page(__name__, path="/logout")


def layout():
    if current_user.is_authenticated:
        logout_user()
    return html.Div(
        [
            html.Div(html.H2("You have been logged out - Please login")),
            html.Br(),
            dcc.Link("Home", href="/"),
        ]
    )

****************page_container.py :


import dash
from dash import dcc, html


def main_layout(app):
    app.layout = html.Div(
        [
            dcc.Location(id="url"),
            html.Div(id="user-status-header"),
            html.Hr(),
            dash.page_container,
        ]
    )

@anarchocaps

Check the layouts in your pages. I dont see anything in the two codes that you posted.

thanks, @jinnyzor. I went back and revised the question so that it has all the modules, including the layouts.

I feel like I am really close, since the app work, but the dcc.Location is now missing from the top of the page.

Also my linter is showing the update_authentication_status callback function in login_callback.py as greyed-out, as if it is not being referenced.

@anarchocaps

I just ran your code and it seems like I am logging in and out, with navigation just fine. I installed all the packages afresh as well.

What issues are you running into?

The update_authentication_status is working fine for me and returning the login / logout respectively.

@jinnyzor - this is a little frustrating… I am continuing to get the same error that I posted above. The fact that you are not tells me it has something to do with my local setup ie. my IDE?

Try navigating directly to /login. See what that says.

@anarchocaps,

If this doesnt work, maybe try updating / reinstalling the packages.

@jinnyzor I got it to work! but am not sure what fixed it:

  • I tried using powershell terminal instead of Git Bash (I have noticed that since using Git Bash, it doesn’t operate as smoothly as powershell)
  • I moved server = Flask(__name__) to login_callback.py, and am just importing server into app.py. This allows the entire script in login_callback.py to no longer be within a function.

I am now, however experiencing a new issue that I cannot solve concerning a redirect with the login button, but that may be more appropriate for it’s own separate post…

Thank you again for the help!

Ah, nice.

How were you running python from the Bash? It could have been an issue with how the path’s were working.

my process (on windows 11):

  1. right click project folder > show more options > Git Bash Here
  2. $ python app.py

Its the only way I know how… :slight_smile: