In my Flask API, I'm trying to get sessions to stay permanent, so that it will still be there even after browser closes. My front-end is written in React, and uses the Fetch API to make requests. However, after testing out what I have so far, it doesn't seem to work. My code (left out some irrelevant database stuff that works):
@bp.route('/login', methods=('POST',))
def login():
...
error=None
res = {}
db = get_db()
cursor = db.cursor(dictionary=True)
...
user = cursor.fetchone()
...
if error is None:
session.clear()
session.permanent = True
session['userID'] = user['id']
current_app.logger.debug(session['userID'])
res['actionSuccess']= True
else:
res['actionSuccess'] = False
res['error'] = error
return jsonify(res)
So far, I can tell that sessions is indeed storing the userID value. I then write another route to tell me if an userID value is stored in session, like so:
@bp.route('/init', methods=('GET',))
def init():
userID = session.get('userID')
if userID is None:
res = {"signedIn": False}
else:
res = {"signedIn": True, "username": userID}
return jsonify(res)
However, everytime I make the call to '/init', it returns False even though I previously signed in. I don't know why session isn't permanent here. Is it because I'm running the client locally on my machine? Do I need to allow cookies somewhere on my Chrome browser? I used Chrome to look into the cookies stored for the client, and no "sessions" was stored there. Do I have to something extra on the front-end to store the cookies/session, or do they store automatically? Am I misunderstanding the usage of Flask sessions?
Found out why sessions wasn't working after a lot of research! Flask sessions are essentially cookies, and I was using the Fetch API to perform CORS operations. Fetch() by default does not allow cookies to be received or sent, and must be configured in order to use Flask sessions.
On my React.js client, I did this by setting 'include' for 'credentials':
fetch(url, {
method: 'POST',
mode: 'cors',
body: JSON.stringify(loginObj),
credentials: 'include',
headers: {
'Content-Type': 'application/json'
}
})
.then(res => res.json())
...
Because of this configuration, the request isn't considered a "simple request", and the client will actually "preflight" the POST request with an OPTIONS request. This means that before my POST request is sent, a OPTIONS request testing to see if the server has the correct access will be sent to my Flask server first.
The preflight OPTIONS request will test to see if the response from the server has the correct headers containing "Access-Control-Allow-Origin", 'Access-Control-Allow-Credentials', and 'Access-Control-Allow-Headers'. If the test sent by the OPTIONS request fails, the actual POST request will not be sent and you'll get a Fetch error.
I then set the headers accordingly on my Flask server like so:
@bp.route('/login', methods=('POST','OPTIONS'))
def login():
if request.method == 'OPTIONS':
resp = Response()
resp.headers['Access-Control-Allow-Origin'] = clientUrl
resp.headers['Access-Control-Allow-Credentials'] = 'true'
resp.headers['Access-Control-Allow-Headers'] = "Content-Type"
return resp
else:
'''
use session for something
'''
res['actionSuccess'] = False
js = json.dumps(res)
resp = Response(js, status=200, mimetype='application/json')
resp.headers['Access-Control-Allow-Origin'] = clientUrl
resp.headers['Access-Control-Allow-Credentials'] = 'true'
resp.headers['Access-Control-Allow-Headers'] = "Content-Type"
return resp
Take note that 'Access-Control-Allow-Credentials' was set to 'true' as opposed to the Python boolean True, as the client will not recognize the Python boolean.
And with that, a Flask Session object should be stored in your cookies.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With