'Options for User Session Management with React and Flask

After days of reading through the source code of Flask and Flask-Login and trying to figure out why my user sessions aren't being visibly accessible outside the route where the session is set, I am convinced that the response sent by Flask API is causing the issue.

I have tried to reverse engineer some of the tutorials that are present on the internet to use Flask-Login, however, most of them rely on using Jinja templates. This posed a challenge as I had to figure out a way to make it work with a React frontend. I initially used the examples with jinja templates as an example as I was building out the React frontend and I was immediately faced with some issues.

I have the following routes: /login and /logout. I also utilized current_user from Flask-Login to access session details. So for example:

  • If the /login route is accessed and current_user is authenticated, then nothing happens.
  • If the /login route is accessed and current_user is not authenticated, then the authentication logic is run, session is now updated to include the authenticated user and user is logged in
  • If the /logout route is accessed and current_user is not authenticated, nothing happens
  • If the /logout route is accessed and current_user is authenticated, then the session is cleared and used is logged out

I was able to get the first two from above to work, however, when the user is logged in, the session appears to not been saved and I am not able to log out.

Below are two different code snippets:

Jinja Templates

@auth.route('/login')
def login():
    return render_template('login.html')

@auth.route('/login', methods=['POST'])
def login_post():
    email = request.form.get('email')
    password_hash = request.form.get('password')

    user = RegisteredUser.query.filter_by(email=email).first()

    if user is not None and user.check_password(password_hash):
        login_user(user)
        return redirect(url_for('main.profile'))

    return redirect(url_for('auth.login'))

JSON Object Response

@auth.route('/login', methods=['GET', 'POST'])
def login_post():
    if current_user.is_authenticated:
        return jsonify({ 'login' : True })    

    data = request.get_json()
    user = RegisteredUser.query.filter_by(email=data['user']).first()

    if user is not None and user.check_password(data['password']):
        login_user(user)
        return jsonify({ 'login' : True })

    return jsonify({ 'login' : False })

The code example using Jinja templating works and the session is saved after all the code is finished executing when the /login route is accessed. However, this is not the case for the code returning JSON data.

This leads me to believe that in order to properly use Flask-Login, developers would need to make custom Flask Response objects (i.e. look like - redirect(url_for(...)) or render_template(...)) and reconstruct how the response would look like if Jinja templating was used.

I was wondering if anyone else had experienced this or is there a different path I could take that would be better.

Thanks.

Edit:

I am now open to different possible solutions. If I don't use flask-login, what are other options for user session management for React and Flask?

I have looked into flask-security, flask-user, flask-session, but they don't seem to be maintained as much as flask-login.

I am ideally looking at cookie-based solutions.



Solution 1:[1]

For my solution, I scrapped using Flask-Login and went another path.

Since what I did may unintentionally reveal confidential stuff, I will provide a high-level overview. Feel free to ask questions as I have saved all the resources that led me to figuring out what to do.

I decided to use Google's Firebase API (For the frontend) and Admin SDK (For the backend).

Admin SDK uses session cookies, so if you have two separate servers here are some topics that you might want to look into

  • Cross-origin resource sharing
  • Cross-domain cookies
  • Subdomains (This relates to the point above)

Because I am using cookies, something that I had to be mindful of is CSRF attacks. For this, I am using Flask-WTF extension because it has an implementation to prevent it.

However, in the event of any errors such as:

  • CSRF token is missing
  • CSRF session token is missing
  • CSRF session token expired
  • etc

Flask-WTF by default doesn't return anything back to the client (at least if running two separate servers). When an error occurs, it just displays a 4XX page. As a result, I forked the repository source code such that I can get some kind of 4XX response to the client.

I am not a security expert by any means, but I have learnt quite a bit of small things along the way. Be sure that a secure connection is always in place. Also, make sure when you are setting up subdomains, be very careful as you don't want to fall victim to subdomain takeovers.

Cheers!

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 jeff