Issue
Please Note: there are many questions like this one but none of them have worked for me which is why I am asking this.
My REST API is a Flask application. My /login
endpoint uses JWTs and sets the access and refresh tokens in the client's cookies.
I am running Flask on 127.0.0.1:5000
and the frontend on 127.0.0.1:8080
.
Backend Code
@auth.api(
http_path='/login',
http_method='POST',
)
def login(email, password):
# ...
access_token = create_access_token(identity=user.id, fresh=True, expires_delta=app.config['JWT_ACCESS_TOKEN_EXP'])
refresh_token = create_refresh_token(identity=user.id, expires_delta=app.config['JWT_REFRESH_TOKEN_EXP'])
resp = jsonify({'login': True})
set_access_cookies(resp, access_token)
set_refresh_cookies(resp, refresh_token)
# ...
return resp
My app has CORS enabled:
from flask_cors import CORS
# ...
cors = CORS()
def create_app():
app = Flask(__name__)
# ...
cors.init_app(app)
# ...
return app
Client Code
$.ajax({
method: "POST",
url: "http://127.0.0.1:5000/login",
data: JSON.stringify({email: 'myemail', password: 'mypassword'}),
contentType: 'application/json; charset=utf-8',
dataType: 'json',
success: function (data) {
console.log('logged in!');
}
});
The Problem
The cookies are not being set in Firefox, Chrome nor Edge. However I can see the cookies in the Response Headers when using the browser's dev tools, but nothing appears in the Storage section, under Cookies.
I have tried multiple things:
- setting
cors=CORS(supports_credentials=True)
on the backend and settingxhrFields:{ withCredentials: true}
on the frontend. - setting
crossDomain: true
on the frontend - making sure no cookies are being blocked by the browser's settings
- running specifically on
127.0.0.1
instead oflocalhost
Do I need some sort of proxy for the frontend? There must be an easier solution.
Solution
So after a lot of headaches, I figured out what's wrong. It's partially my fault but also a potential bug.
I am using the flask-jwt-extended
library for my JWT authentication; there are some environment variables regarding JWTs whose values are used in the set_access_cookies
function.
Typically, your Flask application config is the place for these variables, however, I had opted for using a separate .env file and loading my environment variables from it. The issue was that for things like MY_VAR=False
, MY_VAR
would get the string value "False" instead of a boolean flag.
This manifested itself particularly poorly for JWT_COOKIE_SECURE
- the value of this variable is used in the set_access_cookies
function which itself uses the set_cookie
function for a Request
object - if you pass a string for the Secure field, it garbles the cookie and your browser will most likely ignore it.
So a garbled cookie would look something like this:
('Set-Cookie', 'access_token_cookie=yourtoken; Domain=localhost.example.com; Secure; HttpOnly; Path=/')
Notice how the Secure
field is empty, when it should either be a boolean or not there at all (if false).
I hope this helps!
Answered By - turnip
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.