Flask session not preserved on mobile browser - javascript

I'm new to Flask and I decided that the best way to learn it is to jump straight in the deep end and try to make an app from scratch. I have successfully made it and it works perfectly on desktop, but sessions aren't maintained when I try to use my mobile. I have an iPhone 12 and I've tried it on Chrome, Safari, Opera and Firefox and none work. It works on Edge, Chrome and Firefox (those were all I tested on) on my desktop PC.
Here is my init file:
from flask import Flask, jsonify, request, make_response, render_template, session, redirect, url_for
app = Flask(__name__)
app.config['SECRET_KEY'] = "Actual one is a fixed, random string like 89hsfd98sdhf89u983qwj9"
#app.route('/')
def index(): return render_template('index.html')
# This is where the session is created
#app.route('/player/add', methods=['POST'])
def player_add():
req = request.get_json()
session['session_key'] = _generate_session_key(STRING_LENGTH)
session['name'] = req['name']
return make_response(jsonify({"result": player.player_add(req['name'], session['session_key'])}), 200)
# This is the important one where the session is called
#app.route('/player', methods=['GET'])
def get_session_name():
try:
if not _valid_session(): return make_response(jsonify({'status': False}), 200)
data = player.player_info(session['name'])
except: data = {'status': False}
return make_response(jsonify(data), 200)
"""
Many more app routes
"""
if __name__ == '__main__':
app.run()`
If it helps, I communicate with the Flask server from the frontend using fetch like this:
fetch(`${window.origin}/player`, {
method: "GET",
credentials: "include",
cache: "no-cache",
headers: new Headers({
"content-type": "application/json"
})
}).then(...)
If a session has been made, then the app will display the user's entered name. If not, it will prompt the user to enter their name (and start a session). Again, it works perfectly on desktop browsers, but with mobile it just responding as if no session has been created (which is {'status': False}).
There's probably a lot of bad practice here, but I've been researching for a while and I have no idea why it would only work on desktop and not mobile. Please let me know if I should provide more information.
The site is being hosted on PythonAnywhere, if that helps. No errors on the console or from the backend.
Any help or guidance would be greatly appreciated! Even if the problem persists, at least being able to use it on mobile with a few temporary settings changes would be alright.
I've tried:
Using credentials: "same-origin" when calling fetch
Setting:
app.config["SESSION_TYPE"] = "filesystem
app.config["SESSION_PERMANENT"] = False
app.config['SESSION_COOKIE_SECURE'] = True
app.config['SESSION_COOKIE_SAMESITE'] = 'None'
Using HTTPS instead of HTTP
Disabled 'Prevent Cross-Site Tracking' (essentially allowing for third-party cookies)
Clearing the sessions and regenerating the SECRET_KEY
Clearing all stored cookies and history in the browser

Related

Login to Amazon with Cookies and Scrape Kindle Highlights

Amazon Kindle has the following site where you can access highlights from your Kindle: from https://read.amazon.com/notebook.
I want to scrape these highlights but none of my attempts have worked so far. I know that this should be possible because Readwise does this and a note-taking app named Reflect.app has a chrome extension where they also managed to do this.
I found this URL that returns relevant JSON data with a list of books read (this is the first piece of the puzzle): https://read.amazon.com/kindle-library/search?query=&libraryType=BOOKS&sortType=recency. If I go to the Network tab and copy the request as a cURL and import it on Postman, the request ends up working successfully.
I've tried making the request on the client side with Fetch and Axios and with Python on the backend but neither approach has worked.
When I make the request on the client side, I get the error Access to fetch at 'https://read.amazon.com/kindle-library/search?query=&libraryType=BOOKS&sortType=recency' from origin 'http://localhost:3000' has been blocked by CORS policy: The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'.
Here is the code I am using, which I got by copying the request on the Network tab as a fetch request:
const request = await fetch(
"https://read.amazon.com/kindle-library/search?query=&libraryType=BOOKS&sortType=recency",
{
headers: {
accept: "*/*",
"accept-language": "en-US,en;q=0.9",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "none",
},
referrerPolicy: "strict-origin-when-cross-origin",
body: null,
method: "GET",
mode: "cors",
credentials: "include",
}
);
I've tried many iterations of this. For example, I have tried setting the mode to "no-cors" but then I get an empty response. I also tried adding the session cookie information in the header of the request but that doesn't make a difference.
So I tried making the request from the backend using Python and Flask. When I do a get request using the Requests library, the url gets redirected to the amazon login page. I tried adding the session cookie as well and that didn't make a difference.
This is the code I have:
#api.route('/data')
def data():
session = requests.session()
request_url = 'https://read.amazon.com/kindle-library/search?query=&libraryType=BOOKS&sortType=recency'
headers = {
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"cookie": "<cookie session info>"
}
params = {
"referrerPolicy": "strict-origin-when-cross-origin",
}
req = session.get(request_url, headers=headers, params=params)
I think what I need to do is find a way to authenticate with Amazon when making the request and I believe the way to do that is with a cookie. Right now I am just trying to hardcode my own cookie information but I would need a way to get the cookie from the user's session as well.
I'm not sure what I'm missing here. Or maybe there is a better way to do what I want to do?

Django doesn't receive cookies from request

I'm working on a Nuxt/Vue app that uses Django on the backend. I'm using the standard Django Session authentication. The problem with my code is that the user's are always logged out, according to Django, because Django doesn't see any cookie in the request.
Basically i created an API endpoin that should return whether or not the user is authenticated, but since Django doesn't see any sessionid cookie in the request, the user will always be unauthenticated to the backend.
def checkAuth(request):
print(request.COOKIES)
response = {'state': str(request.user.is_authenticated), 'username': str(request.user)}
return JsonResponse(response, safe=False)
If i print request.COOKIES it returns {}.
Here is how i send the request from the frontend, where i'm using Axios:
return axios({
method: 'get',
url: 'http://127.0.0.1:8000/checkAuth',
withCredentials: true,
}).then(function (response) {
console.log(response)
}).catch(function (error) {
console.log(error)
});
The problem should not be from the frontend, since on Chrome i can see the session cookie and the csrf cookie correctly. I already tried to disable any possible CORS security setting on Django. The frontend is running on 127.0.0.1:3000 and the Django app on 127.0.0.1:8000.
Why doesn't Django see the cookies? Any kind of advice is appreciated

How to prevent session from external site from timing out?

I am using nodeJS to create an application to create simplified experience of our college's student portal which uses session based authentication. I am aware that in python, we have requests module, from which, we can use requests.Session() object to make get, post etc. requests from the same session. What is the NodeJS equivalent of this?
Also, the portal is set to end the session after 15 mins of inactivity. Is there something I can do to avoid this i.e. generate a never ending session?
Got the solution for this.
I used axios to make requests.
const axios = require("axios");
const axiosCookieJarSupport = require("axios-cookiejar-support").default;
const tough = require("tough-cookie");
axiosCookieJarSupport(axios);
const cookieJar = new tough.CookieJar();
axios.get(url, {
jar: cookieJar,
withCredentials: true,
}).then((res)=>{
//do your stuff
//any set-cookie headers will be automatically processed.
//use the same config for further requests,
//it will automatically send the cookies alongside the requests.
})

Unable to set cookies with Spring Boot on the serve side

I'm using Spring Boot on server side. When I'm adding cookie to response it adds Set-cookie header with right value but when browser receives response it displays that header but won't set the cookie. Also Postman stores all cookies fine.
Spring
public ResponseEntity<?> authenticate(#RequestBody AuthenticationRequest request, HttpServletResponse response) throws Exception {
Cookie cookie = new Cookie("token", "COOKIE_VALUE");
cookie.setHttpOnly(true);
cookie.setSecure(false);
response.addCookie(cookie);
return ResponseEntity.ok("Connection succeeded");
}
JSfetch (from React app from different port)
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
var raw = JSON.stringify({"username":"TestUser","password":"pwd"});
var requestOptions = {
method: 'POST',
headers: myHeaders,
body: raw,
redirect: 'follow'
};
fetch("http://IP_ADDRESS:8080/authenticate", requestOptions)
Chrome's seeing cookie in the headers
But it won't add it to the storage
So does Firefox. What did I miss? Is there a solution to this?
I'm using my internet ip address in fetch with port 8080 - not localhost. But localhost didn't do the trick either.
UPD. It seems working though when the ports are the same. I tried to return jsp page instead and that page executes the fech statement and it has stored the cookie. So solution to this is probably to compile react app and put it on the server. Anyway how to deal with cookies when the ports are not the same?
Cookies are accessibles only if the JS is provided from the same origin (host:port).
You may use URL rewriting approach to use the same origin for your assets and API. You may look at devServer if your are using Webpack.
Consider LocalStorage that offer more modern approach to deal with it as well.
Regards.
Chrome has changed its recent policies not to support localhost or development cookies, so you have to work around and play it with HTTP cookie
ResponseCookie resCookie = ResponseCookie.from(cookieName, cookieValue)
.httpOnly(true)
.sameSite("None")
.secure(true)
.path("/")
.maxAge(Math.toIntExact(timeOfExpire))
.build();
response.addHeader("Set-Cookie", resCookie.toString());
This thing works for me but, make sure it only works for https (not HTTP) and this thing is a makeover for development purposes only, once if you host your server chrome allows response cookies else it just blocks all kinds of HTTP cookies.
Ok, changing this in spring boot
#CrossOrigin
to this
#CrossOrigin(origins = "http://MY_IP_ADDRESS", allowCredentials = "true")
saved my day. I still don't get it why it didn't work when I set the headers manually as follows in the post mapping method
response.addHeader("Access-Control-Allow-Credentials", "true");
response.addHeader("Access-Control-Allow-Origin", "http://MY_IP_ADDRESS");

Cookie set by Flask app sent but not stored

I have a backend Flask app running on localhost:3000 and a React front-end app running on localhost:5000. In my backend app I am using Flask's 'Response.set_cookie' to set a cookie:
resp = make_response({}, 200)
resp.set_cookie('my_cookie_name', 'my_val', max_age=604800, domain='127.0.0.1', samesite='Lax', secure=None, httponly=None)
I am also allowing cross-origin for all responses in my flask app as follows:
# Child class of Flask to override some features
class TailoredFlask(Flask):
# Override make_response
def make_response(self, rv):
# Call default version from partent
resp = super().make_response(rv)
# Add CORS header to every response
resp.headers["Access-Control-Allow-Origin"] = "*"
resp.headers["Access-Control-Allow-Methods"] = "GET,POST,OPTIONS,HEAD"
resp.headers["Access-Control-Allow-Headers"] = "Origin, X-Requested-With, Content-Type, Accept, Authorization"
return resp
My client accesses my flask cookie endpoint with a call to fetch.
In the Chrome dev tools I can see that the cookie is sent with the HTTP response from my backend. It is visible when on the Network->Cookies tab when I select the request to my backend. However, if I go to the Application tab in the dev tools, my cookie is not there.
It seems like chrome is silently discarding my cookie. I have seen several simiar issues here on SO but none of them seem to explain what is going on or provide a solution to my issue.
I'm also confused about the cookie options. There is a 'domain' option which I've read is to allow cross domain operation for the cookie. However, everything is running on localhost so I feel that I shouldn't need this unless the port is causing issues. However, I have also read that the port should not be included in the cookie 'domain' field.
If anyone can help to explain this to me I would greatly appreciate it because I'm just going round in circles with this stuff.
One more thing to note: I am pointing the browser at 'localhost', but the API call to my backend and the cookie domain both use '127.0.0.1', since I've read elsewhere that the 'domain' field must have at least two dots in it. (I don't have a choice in the browser URL since I am using AWS cognito login UI to redirect to my app after login. Cognito allows http for 'localhost', but only allows https for '127.0.0.1' so I have to use 'localhost' for development.) Could the missmatch between the browser url and cookie domain be causing this issue? Or is there something else that I'm missing?
Ok, so I think I now understand what's going on here, although I don't think there's a fix for my specific problem. As described in this thread browsers (including Chrome) will not allow a domian of 'localhost' within a cookie (I just wish there was a message in the console or something to indicate why the cookie is not being saved, rather than a silent fail!)
There are various suggestions for workarounds, such as using '.app.localhost' to access the application. Unfortunately this is not an option for me as I am redirecting to my front-end app from AWS Cognito, and the only domain that is supported with HTTP (rather than HTTPS) is 'localhost'. Variants such as '.app.localhost' or '127.0.0.1' are not allowed.

Categories

Resources