Unsuccessful to logout app

Hi,

Previously, my app able to perform logout function.
However, start from few days ago, the same app is keep on login to the app even I had clicked the logout.

I don’t know which part is going wrong. I had tried to run the old version app, same issue occur.
Even I added the the @login_required in my route, it still not work (keep on login).

Kindly advise.

Hello @beginof,

You need to log out the user.


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

dash.register_page(__name__)


def layout():
    if current_user.is_authenticated:
        logout_user()
    return html.Div(
        [
            html.Div(html.H2("You have been logged out - You will be redirected to login")),
            dcc.Interval(id={'index':'redirectLogin', 'type':'redirect'}, n_intervals=0, interval=1*3000)
        ]
    )

Hi @jinnyzor ,

Yes, I had add the logout_user() in my app, but it will still keep on login to the home page :sob:

However, I found out that it may due to the cookies is saved in my browser, as for example, I had tried to use 2 different browser, firefox and firefox private, the app in firefox will have auto login issue, but firefox private do not have.
So, I not sure is it the saved cookies affect my app :thinking:

If it is due to the saved cookies issue, any method I can add to the app to clear the saves cookies to prevent the auto login issue?

Cookies shouldn’t matter. That’s how Flask login works.

I don’t know what else you have going on.

Can you host your repo and share it?

Or give more of your code?


Initially, I also won’t think it is due to the cookies issue, but, I tried to clear the cookies, then the app can work as usual :rofl: :rofl:


Any way, please advise what is the main root cause so I can avoid such case happened again. Previously, it is no issue in my app, but this such case happened from last week ago.


Here is my code:

app.py

content = html.Div(id="page-content", children=[], className = 'content')




app.layout = dbc.Container([
    dcc.Location(id="url"),
    
    html.Br(),     
        
    html.Div([
        
        dbc.Button(html.I(className ="bi bi-list menu-btn-img " ),
                   id="open-offcanvas", n_clicks=0, className = 'menu-btn'),
        
        dbc.Offcanvas([
            sidebar(),    
            content
            ],
            
            id="offcanvas",
            # title="Welcome to ...",
            is_open=False,
            scrollable = True,
            backdrop="static",
            className="offcanvas-style"
            ),
        ],
        style={'position': 'fixed'}, 
        ),   

    
    html.Div(id="user-status-header"),

    dbc.Row([
        dash.page_container
        ], 
        
        )
    
    ], fluid=True)




@app.callback(
    Output("offcanvas", "is_open"),
    Output("offcanvas","children"),
    Input("open-offcanvas", "n_clicks"),
    [State("offcanvas", "is_open")],
)
def toggle_offcanvas(n1, is_open):
    if n1:
        return not is_open, [sidebar(), content]

    
    return is_open, dash.no_update



@app.callback(
    Output("user-status-header", "children"),
    Output('url','pathname'),
    Output('open-offcanvas','style'),
    Input("url", "pathname"),
    Input({'index': ALL, 'type':'redirect'}, 'n_intervals')
)


def update_authentication_status(path, n):
    ### logout redirect
    if n:
        if not n[0]:
            return '', dash.no_update, {'display':'none'}
        else:
            return '', '/login', {'display':'none'}


    ### test if user is logged in
    if current_user.is_authenticated:
        if path in restricted_page:
              if 'access_level' in restricted_page[path]:
                   # if (VALID_USERNAME_PASSWORD[current_user.id]['role'] not in restricted_page[path]['access_level']):  
                   if (VALID_USERNAME_PASSWORD[current_user.id]['role_id'] not in restricted_page[path]['access_level']):  

                      return (dcc.Link("",id = 'logout', href="/logout", style={'position': 'fixed'})), '/permission', {'display':'initial'}
        
        
        if path == '/login':
            return (dcc.Link("" , id = 'logout', href="/logout", style={'position': 'fixed'}), '/', {'display':'initial'})
        
        if path == '/2fa':
            return (dcc.Link("", id='logout', href="/logout", style={'position': 'fixed'})), dash.no_update, {'display': 'none'}
        
        return (dcc.Link("",id = 'logout', href="/logout", style={'position': 'fixed'}), dash.no_update, {'display':'initial'})
    
    
    else:
        ### if page is restricted, redirect to login and save path
        if path in restricted_page:
            session['url'] = path
            return dcc.Link("login", href="/login"), '/login', {'display':'none'}


    ### if path not login and logout display login link
    if current_user and path not in ['/login', '/logout']:
        return dcc.Link("login", href="/login"), dash.no_update, {'display':'initial'}

    ### if path login and logout hide links
    if path in ['/login', '/logout']:
        return '', dash.no_update, {'display':'none'}



server.py

# Load the .env file
load_dotenv()


# Access the value of SECRET_KEY environment variable
SECRET_KEY = os.environ.get('SECRET_KEY')


# Exposing the Flask Server to enable configuring it for logging in
server = Flask(__name__)



@server.route('/change-password', methods=['GET', 'POST'])
@login_required
def change_password():
    if not current_user.is_authenticated:
        return redirect('/login')
        
    # Store user ID
    username = current_user.id
    
    if request.method == 'POST':
        new_password = request.form['new_password']
        confirm_password = request.form['confirm_password']
        
        now = datetime.now()
        
        if not is_valid_password(new_password):
            return """Password does not meet policy requirements. <a href='/change-password'>Reset again</a>"""
        
        # Check if both passwords match
        if new_password != confirm_password:
            return """Passwords do not match! <a href='/change-password'>Reset again</a>"""
        
        # Hash the new password
        hashed_pwd = hashlib.sha256(new_password.encode()).hexdigest()
        
        # Update password in database
        cursor.execute(update_exist_password, (hashed_pwd, username, now, username))

        conn.commit()
        
        # Update stored password for authentication
        VALID_USERNAME_PASSWORD[username]['password'] = hashed_pwd


        return redirect('/')

    
    return redirect ('/')




@server.route('/2fa', methods=['POST'])
@login_required
def two_factor_authentication():
    # if not current_user.is_authenticated:
    #     return redirect('/login')
    
    username = current_user.id

    
    if request.method == 'POST':
        entered_token = request.form['token']
        
        cursor.execute(get_exist_secret, (username,))
        shared_secret = cursor.fetchone()[0]       

        
        
        totp = pyotp.TOTP(shared_secret)
        if totp.verify(entered_token):        

            token_update_at = datetime.now()            
            cursor.execute(update_date_token, (token_update_at, username))
            conn.commit()            
            
            return redirect('/')

        return '''Invalid verification code. Please <a href='/2fa'>try again</a>.'''
    
    return redirect ('/2fa')




@server.route('/login', methods=['POST'])
def login_button_click():
    if request.form:
        username = request.form['username']
        password = request.form['password']
        
        if not is_valid_password(password):
            return """Password does not meet policy requirements <a href='/login'>login here</a>"""
        
        
        user_data = VALID_USERNAME_PASSWORD.get(username)
        
        if user_data is None:
   
            return """Invalid username and/or password <a href='/login'>login here</a>"""
        
        stored_pwd = user_data['password']
        hashed_pwd = hashlib.sha256(password.encode()).hexdigest()
        
        
        if stored_pwd == hashed_pwd:
            
            if user_data['status'] == 0:  # Check if the user is inactive
                return """Inactive user. Please contact the administrator to activate your account."""
           
            last_login_date = datetime.now()
            cursor.execute(update_date_last_login, (last_login_date,username))
            conn.commit()     
            
            cursor.execute(get_date_token, (username,))
            token_update_at = cursor.fetchone()[0]
                       
            login_user(User(username))
                       
            if 'url' in session:
                if session['url']:
                    url = session['url']
                    session['url'] = None
                    return redirect(url) ## redirect to target url
            
            
                        
            if token_update_at is None:
                token_update = last_login_date
                cursor.execute(update_date_token, (token_update, username))
                conn.commit()
                                
                return redirect('/2fa')
            
            # if shared_secret is None:
            #     return redirect('/2fa')
            
                        
            token_expired_at = token_update_at + timedelta(days=30)
            
            if last_login_date >= token_expired_at:
                return redirect('/2fa')
            
            
            
            return redirect('/')

        
        
        return """Invalid username and/or password <a href='/login'>login here</a>"""



# @server.route('/logout', methods=['GET'])
# def logout():
#     logout_user()
#     response = redirect('/login')
#     response.delete_cookie('session', path='/')  # Clear the session cookie
#     return response





app = dash.Dash(
    __name__, server=server, use_pages=True, suppress_callback_exceptions=True,
    external_stylesheets=[dbc.themes.BOOTSTRAP, dbc.icons.BOOTSTRAP, 
                           # "/assets/display.css"
                           ],
    meta_tags=[{'name': 'viewport',
                'content': 'width=device-width, initial-scale=1.0'}] #, maximum-scale=1.0, minimum-scale=0.5
)



df = masteruser()
VALID_USERNAME_PASSWORD = df.set_index('username').transpose().to_dict()


server.secret_key = SECRET_KEY



# 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
        self.role_id = VALID_USERNAME_PASSWORD[username]['role_id']





@login_manager.user_loader
def load_user(username):
    """This function loads the user by user id. Typically this looks up the user from a user database.
    We won't be registering or looking up users in this example, since we'll just login using LDAP server.
    So we'll simply return a User object with the passed in username.
    """
    return User(username)


login.py

dash.register_page(__name__,
                   path='/login',  # '/' is home page and it represents the url
                   name=' login',  # name of page, commonly used as name of link
                   title='Login',  # title that appears on browser's tab
)



# Login screen
layout = html.Div([
        html.Form([
            html.Div([
                html.Img(src = "/assets/mi7.png"),
                ]),
            
            html.Div([
                html.Div([
                    html.H1('Here Sign In'),
                    
                    html.H3('Username'), 
                    dcc.Input(placeholder="Enter username", type="text", id="uname-box"
                          , name='username'), 
                    
                    html.H3('Password'),
                    dcc.Input(placeholder="Enter password", type="password", id="pwd-box"
                          , name='password'),
                    
                    
                    html.Button('Login',n_clicks=0, type="submit", id="login-button" ),
                    
                    ]), 
                

                ]),
            

                
                html.Div(children="", id="output-state")
                
                
                ], method='POST') 
    
    
    ])



logout.py

dash.register_page(__name__,
                   path='/logout',  
                   name=' Logout', 
                   title='Logout',  
)


def layout():
    if current_user.is_authenticated:
        logout_user()
        
    return dbc.Container([
                dbc.Row([
                    dbc.Col([
                        html.Div([
                            html.Div([
                                
                                    html.H2("You have been logged out - You will be redirected to login."
                                            , className = 'logout-h'
                                            ),
                                

                                    html.Img(src = "/assets/m10.png", 
                                           ),

                                
                                    ]),
                            
                                dcc.Interval(id={'index':'redirectLogin', 'type':'redirect'}, n_intervals=0, interval=1*2000)
                                ])
                        ])
                    ])
                ])


home.py

dash.register_page(__name__,
                   path='/', 
                   name=' Home',  
                   title='Main',  
                   icon="bi bi-house", 

)

require_login(__name__, 
              access_level= package1
              )



greeting = get_time_greeting()



def layout():
    if not current_user.is_authenticated:
        return html.Div(["Please ", dcc.Link("login", href="/login"), " to continue"])
    
    return html.Div([
        dbc.Container([
   
            dbc.Row([
                dbc.Col([
                    html.H2(greeting + ', ' + current_user.id.title ()+ '!', 
                            className = 'home-H'),
                    ], className = 'home-H-col'),
                ]),
            

            dbc.Row([
                html.H4('Line1'),
                    
                html.Hr(),
                
                dbc.Col([
                    dbc.Card([
                            dbc.CardImg(src="/assets/scr1.jpg", top=True),
                            dbc.CardBody([
                                    html.H5("Card1"),
                                    html.P(
                                        "description1",
                                       
                                    ),
                                    dbc.Button("Lets go ...", href = '/source1'),
                                ]),
                        ],
                        
                        ),

                    
                    ], width = 'auto'), 
                
                ]),
            
            
            dbc.Row([
                dbc.Col([
                    html.H2(greeting + ', ' + current_user.id.title ()+ '!', 
                            className = 'home-H'),
                    ], className = 'home-H-col'),
                ]),
            

            dbc.Row([
                html.H4('Line2'),
                    
                html.Hr(),
                
                dbc.Col([
                    dbc.Card([
                            dbc.CardImg(src="/assets/scr2.jpg", top=True),
                            dbc.CardBody([
                                    html.H5("Card2"),
                                    html.P(
                                        "description2",
                                       
                                    ),
                                    dbc.Button("Lets go ...", href = '/source2'),
                                ]),
                        ],
                        
                        ),

                    
                    ], width = 'auto'), 
                
                ]),
            
            html.Br(),
            html.Br(),
            
            
            ]),
            
        ])