Dash App Pages with Flask Login Flow using Flask

Nice aspect of Dash security… But, what if we put protection inside @callback function also?

Sth like this also in callback…

if not current_user.is_authenticated:
    return html.Div(["Please ", dcc.Link("login", href="/login"), " to continue"])

What is your opinion?

Hi All

Authenticating username and passwords, i realised that if the developer is not careful we could land with a netflix effect. multiple users joining with one login details

Say you want to restrict exclusive access to your users but they are very kind and share their login details to others .

So I thought in an alternative way, the following are the steps:

  • Login page - > generic landing page
  • if username and password are not in records → go to generic landing page
  • if username and password match records:
    Generate a random string of characters and save in your username records, AND send the user to an url weblink with an address like… “www.webapp/username/random_string/”
  • Username lands back to the page with “webpage/username/random_string”.
    with a callback with Input(‘url’, ‘pathname’), read the pathname, split by “/” and catch the username, and the random key
  • check in the register that username has that specific key in your records and send to the customised page
  • in that way, if the users shares their login details to others, there wont be a chance of having two users at the same time

Another alternative option is a “passwordless” option. Sending automatic emails with a weblink that includes an specific created and saved string of random characters (which the app will check to match the registry once the users clicks on the link in their email, sending the user to the landing page with username and the random key). This case can be very simplistic and very safe if you think about it, user can only indicate their username and the app sends an automatic emails with a random key links for access. In this case security is passed on whichever email company the users has. Since the random key is generated every time a user gets in this option is very safe.

Hello @marcoV,

Thanks for bringing this up.

Using any of those methods, or flask_login by itself will not keep from people doing things to share the logins.

For example, someone could always forward the email that you are sending for the login or do other things. The default session cookie could also be shared and bypass the info. Thus why making sure your cookies are secure is a big thing.

Honestly, if you are wanting to have a subscription based product and using logins and you are concerned with people sharing the app, this is where your legal team needs to step in to make a:

  • privacy policy
  • end user license agreement
  • need to make sure there is a cookie consent

On top of this, because the user has accepted these things, and they use the app in a manner that is not along the agreement, then you can disable their account.

You will also need to be additionally monitoring their account for any activity outside of the norm. Locations aren’t in the same area, etc.

This is all if you mind people sharing their login.

You could also have something in there which limits the sessions to something like 3 to 5. Microsoft uses this for its licenses as well, so might be a good starting point. This would allow people to log in on their phone and computer and one other device.

You would also need a mechanism that checks whether their session should be logged out. :blush:

1 Like

Hello @jinnyzor ,

This code is super helpful. I was able to lock down some pages and still leave some for anonyms user to access.
One thing I noticed:

I have NavLinks on the side which change the page of my multi pages app using the pages feature.
Even tough, I have dcc.Location with refresh and Navlinks with refresh, I won’t get a request I can intercept when switching between pages.

The idea is, that I have a second page, and if the user is not registered when switching from page 1 to page 2, he should get redirected to login.

Are the dash pages not routed through the request process?

They are routed through a post request to an endpoint for dash component updates.

You can intercept by looking for the pathname attribute on the inputs.

1 Like

Ah! No wonder. I was only looking at the get. Thank you!

1 Like

Does the redirect work the same way with the Get request?

What i did looked like this:

@app.before_request
    def check_login():
        block_list = ["/composition"]
        # Check if the requested route is whitelisted
        if request.method == "GET":
            if current_user:
                if request.path not in block_list:
                    return
                elif request.path in block_list and not current_user.is_authenticated:
                    return redirect(url_for("auth_bp.login"))
                else:
                    for pg in dash.page_registry:
                        if request.path == dash.page_registry[pg]["path"]:
                            session["url"] = request.url
                        return
            return
        else:
            if current_user:
                data = json.loads(request.data)
                # Check if the request data contains 'inputs' with a 'pathname' attribute
                inputs = data.get('inputs', [])

                if len(inputs) == 1 and inputs[0].get('id', False) == 'url' and inputs[0].get('value', False) in block_list and not current_user.is_authenticated:
                    # The page redirect goes to block list and user is not authenticated, redirect
                    session["url"] = request.url
                    return redirect(url_for("auth_bp.login"))
                else:
                    for pg in dash.page_registry:
                        if request.path == dash.page_registry[pg]["path"]:
                            session["url"] = inputs[0].get('value', False)
                        return
            return jsonify({"status": "401", "statusText": "unauthorized access"})

The post request will need to be responded to differently.

If it is to the dash update endpoint, then it needs to be returned like:

Otherwise, the redirect can be a regular flask redirect.

Can I use the @before_request in the Post part also to redirect to an external page?

jsonify({"multi": True, "response": {"url": {"pathname": "/login"}}})

I tried to just stet the pathname different, which did not work :smiley:

Depends on the endpoint, if the fetch is not from dash then it won’t understand.

The fetch would have to allow for following redirects I think.