Reputation: 1
I'm developing a web-application which uses Flask server-side and Angular client-side. I need to allow multiuser login in such application and I wanted to use flask session objects to handle login sessions, however sessions are not persisted between requests and everytime they are destroyed.
I've already followed some workaround (by adding specific headers to the request as well as to the response, flask_cors is configured and working).
Any suggestion?
Thanks :)
Edit:
this is my CORS initialization
CORS(app, supports_credentials=True, resources=r'/*')
and my login method that I use to populate the session object is defined as follows:
@app.route('/login', methods=['POST', 'GET'])
def login():
print(request.headers)
_json = request.json
name = _json['username']
password = _json['password']
if name and password:
m = hashlib.md5(password.encode('utf-8')).hexdigest()
results = User.query.filter_by(username=name).first()
if m == results.password:
resp = jsonify("User logged in")
resp.status_code = 200
addRespHeaders(resp)
session.permanent = True
session['username'] = results.username
session['role'] = results.role
session.modified = True
print(session['username'], session['role'])
print(resp.headers)
return resp
else:
resp = jsonify("Attention! Wrong Password")
resp.status_code = 404
addRespHeaders(resp)
return resp
else:
resp = jsonify("Please enter the required fields")
resp.status_code = 404
addRespHeaders(resp)
return resp
finally the addRespHeaders(resp) method:
def addRespHeaders(resp):
resp.headers.add('Access-Control-Allow-Headers', "Origin, Content-Type, X-Requested-With, Accept, x-auth")
resp.headers.add('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS')
When I call the login method and try to print the session['username']
or session['role']
it prints out the right values, but if in another method like:
@app.route('/user/getLoggedIn', methods=['GET'])
def getLoggedInUser():
print('username' in session)
logged_data = UserPersistence.query.all()[0].username
logged_user = User.query.filter_by(username=logged_data).first()
schema = UserSchema()
resp = jsonify(schema.dump(logged_user))
resp.status_code = 200
addRespHeaders(resp)
return resp
the print('username' in session)
returns False
.
Edit2:
This is how my service performing requests looks like:
const httpOptions = {
withCredentials: true,
headers: new HttpHeaders({'Content-Type': 'application/json'})
};
@Injectable({providedIn: 'root'})
export class UserService{
private userUrl = "http://192.168.0.88:5000";
constructor(private http: HttpClient){ }
/** GET users from server */
getUsers(): Observable<User[]>{
return this.http.get<User[]>(this.userUrl + '/users');
}
/** GET user by id. Will 404 if not found */
getUser(id: number): Observable<any>{
const url = `${this.userUrl}/user/${id}`;
return this.http.get<User>(url);
}
getLoggedInUser(): Observable<User>{
const url= `${this.userUrl}/user/getLoggedIn`;
return this.http.get<User>(url);
}
/** POST: login user */
login(username: string, password: string) {
return this.http.post(this.userUrl + '/login', JSON.stringify({"username": username, "password": password}), httpOptions);
}
/** POST: logout logged in user */
logout() {
return this.http.post(this.userUrl + '/logout', httpOptions);
}
/** POST: add a new user to the server */
addUser(user: User) {
return this.http.post(this.userUrl + '/user/add', user, httpOptions);
}
/** PUT: update the user on the server */
updateUser(user: User): Observable<any> {
return this.http.put(this.userUrl + '/user/update', user, httpOptions);
}
/** PUT: update user password */
updatePassword(user: PasswordReset): Observable<any> {
return this.http.put(this.userUrl + '/user/changePassword', user, httpOptions);
}
/** DELETE: delete the user from the server */
deleteUser(user: User | number) {
const id = typeof user == 'number' ? user : user.id;
const url = `${this.userUrl}/user/delete/${id}`;
return this.http.delete(url, httpOptions);
}
}
As far as I know the withCredentials: true
should include credentials.
Edit3:
I managed to persist session data between requests while I'm in localhost in this way:
session_cookie = SecureCookieSessionInterface().get_signing_serializer(app)
@app.after_request
def after_request(response):
origin = request.headers.get('Origin')
if request.method == 'OPTIONS':
response.headers.add('Access-Control-Allow-Credentials', 'true')
response.headers.add('Access-Control-Allow-Headers', 'Content-Type')
response.headers.add('Access-Control-Allow-Headers', 'x-csrf-token')
response.headers.add('Access-Control-Allow-Methods',
'GET, POST, OPTIONS, PUT, PATCH, DELETE')
if origin:
response.headers.add('Access-Control-Allow-Origin', origin)
else:
response.headers.add('Access-Control-Allow-Credentials', 'true')
if origin:
response.headers.add('Access-Control-Allow-Origin', origin)
same_cookie = session_cookie.dumps(dict(session))
response.headers.add("Set-Cookie", f"{same_cookie}; Secure; HttpOnly; SameSite=None; Path=/;")
return response
The problem now is that if I try to login in another PC under the same network, all requests are blocked since cookies are not passed between requests. Any idea? Thanks
Upvotes: 0
Views: 1963
Reputation: 130
Did you added support_credentials=True option when initializing extension?
Here is more from docs:
Using CORS with cookies By default, Flask-CORS does not allow cookies to be submitted across sites, since it has potential security implications. If you wish to enable cross-site cookies, you may wish to add some sort of CSRF protection to keep you and your users safe.
To allow cookies or authenticated requests to be made cross origins, simply set the supports_credentials option to True. E.g.
from flask import Flask, session from flask_cors import CORS
app = Flask(__name__)
CORS(app, supports_credentials=True)
@app.route("/")
def helloWorld():
return "Hello, %s" % session['username']
Check also if your routes are whitelisted for CORS use or decorators are added to routes.
One of the simplest configurations. Exposes all resources matching /api/* to CORS and allows the Content-Type header, which is necessary to POST JSON cross origin.
CORS(app, resources=r'/api/*')
@app.route("/")
def helloWorld():
"""
Since the path '/' does not match the regular expression r'/api/*',
this route does not have CORS headers set.
"""
return '''
<html>
<h1>Hello CORS!</h1>
<h3> End to end editable example with jquery! </h3>
<a class="jsbin-embed" href="http://jsbin.com/zazitas/embed?js,console">JS Bin on jsbin.com</a>
<script src="//static.jsbin.com/js/embed.min.js?3.35.12"></script>
</html>
'''
@app.route("/api/v1/users/")
def list_users():
"""
Since the path matches the regular expression r'/api/*', this resource
automatically has CORS headers set. The expected result is as follows:
$ curl --include -X GET http://127.0.0.1:5000/api/v1/users/ \
--header Origin:www.examplesite.com
HTTP/1.0 200 OK
Access-Control-Allow-Headers: Content-Type
Access-Control-Allow-Origin: *
Content-Length: 21
Content-Type: application/json
Date: Sat, 09 Aug 2014 00:26:41 GMT
Server: Werkzeug/0.9.4 Python/2.7.8
{
"success": true
}
"""
return jsonify(user="joe")
Also, if you are using blueprints, remember to register blueprint to CORS.
Flask-CORS supports blueprints out of the box. Simply pass a blueprint instance to the CORS extension, and everything will just work.
api_v1 = Blueprint('API_v1', __name__)
CORS(api_v1) # enable CORS on the API_v1 blue print
All examples came from Flask-Cors docs. Link to docs
Upvotes: 1