Luis Souza
Luis Souza

Reputation: 385

Flask session don't persist data

I have a Javascript application and a Flask application. When the user send data from Js to Flask, I store it on session and it works fine at a specific route:

@app.route(...)
def user(...):
    session['name'] = name

    print(session['name']) # Works !

But when I tr to get the values on session from another method / route the session is empty:

@app.route(...)
def current():
    print(session.keys(), session.values) # Empty !

I have installed Flask Session and set the config to:

'SECRET_KEY': b'...',

'SESSION_TYPE': 'filesystem', # Memcache, null and redis

'SESSION_PERMANENT': False, # True

And then started the Flask application and it not work. I have also try to set session.modified = True after I add some new value to session and still not work.

I have read lots of threads on Stack Over Flow, Reddit, etc; and nothing worked. Tips please ?

Upvotes: 18

Views: 23044

Answers (4)

Raghav Gohil
Raghav Gohil

Reputation: 1

In Flask, there is a Http-Only cookie (a cookie which can't be accessed in code) which is automatically created when the session is opened. My issue was that the session cookie wasn't saving (I could not find it under applications tab of DevTools). This may be the issue in most cases, so use DevTools to check whether it was saved or not.

Here's how you can fix it:

  • First, you need to enable CORS (like everyone here suggested):
# note that you are allowing all origins here, so put origins explicitly.

CORS(app, supports_credentials=True) # this sets Access-Control-Allow-Credentials header
  • Now, configure the app like this:
app.secret_key = 'any_secret_key'
app.config['SESSION_COOKIE_SECURE'] = True # uses https or not
app.config['SESSION_COOKIE_SAMESITE'] = 'None' # cookie will be sent to another origin(client)

# this ensures that the session cookie is sent from your backend to the frontend securely.
# don't worry, this will also work on http when you are developing.
  • Send credentials: 'include' in all the fetch requests which require session management

Upvotes: 0

Ryan Sherby
Ryan Sherby

Reputation: 1

For anyone reading this post in the future and having difficulties with Daniyal's RuntimeError, the error is most commonly the result of running Session(app) prior to loading your config if you say SECRET_KEY is set.

Upvotes: -1

Jeremie Houet
Jeremie Houet

Reputation: 333

I had the same problem using classic post request in html. The session, which was still storing values in previous route, would empty itself after my post request.

I solved this using:

app.config.update(SESSION_COOKIE_SAMESITE="None", SESSION_COOKIE_SECURE=True)

I am sharing this in case others are facing the same issue.

Upvotes: 9

jera84
jera84

Reputation: 307

TL;DR, enable CORS and credentials support on the back end, and use credentials in the front end code when issuing requests.

I recently ran into a similar issue where I was developing a front end and a back end in separate apps. I noticed that each time I issued a request from the front end client, it would create a new session for each request, which would rapidly bloat the session storage on the back end and made user tracking difficult if not impossible.

I'm assuming that you're Javascript app and Flask app are running separately (i.e., the javascript is not on a template being served by the Flask app and hence the js requests are coming from a different origin).

Suppose we have a simple app with Flask-Session enabled running on port 5000:

from flask import Flask, session
from flask_session import Session

app = Flask(__name__)

SECRET_KEY = "changeme"
SESSION_TYPE = 'filesystem'
app.config.from_object(__name__)
Session(app)

@app.route('/foo')
def foo():
    return session.sid


@app.route('/bar')
def bar():
    return session.sid

Now if we run the app if we navigate to either route on a browser(e.g., http://localhost:5000/foo), we would get the same session id. If you open another tab, open the developer tools and issue the following command in the console, you'd get a cors error:

// Using fetch, you can use jquery or axios    
fetch("http://localhost:5000/foo").then(response => {
    return response.text()
}).then(data => {
    console.log(data)
})

You can fix this easily by installing Flask-CORS and wrapping your app in the CORS class:

from flask import Flask, session
from flask_session import Session
from flask_cors import CORS

app = Flask(__name__)

SECRET_KEY = "changeme"
SESSION_TYPE = 'filesystem'
app.config.from_object(__name__)
Session(app)
CORS(app)

@app.route('/foo')
def foo():
    return session.sid


@app.route('/bar')
def bar():
    return session.sid

Now if you run the javascript fetch function above, it prints out a different session id each time the request is invoked, even for the same route. That's because Flask can't track the session unless you're issuing the requests from the same origin or unless you provide some way for flask to identify the session. You can do this from your JS by allowing credentials to be passed:

fetch("http://localhost:5000/foo",
    { credentials: 'include' }).then(response => {
        return response.text()
}).then(data => {
    console.log(data)
})

However, you will get another CORS error regarding Access-Control-Allow-Credentials. You can fix this in you're Flask app by import the cross_origin decorator, wrapping your routes in the decorator and passing supports_credentials=True to the decorator. The flask code would look something like this:

from flask import Flask, session
from flask_session import Session
from flask_cors import CORS, cross_origin

app = Flask(__name__)

SECRET_KEY = "changeme"
SESSION_TYPE = 'filesystem'
app.config.from_object(__name__)
Session(app)
CORS(app)

@app.route('/foo')
@cross_origin(supports_credentials=True)
def foo():
    return session.sid


@app.route('/bar')
@cross_origin(supports_credentials=True)
def bar():
    return session.sid

Now flask can track the session by the requester (in this case, the browser running the Javascript app).

Upvotes: 19

Related Questions