I can't send an httponly
auth cookie to my flask backend. I've added the secure
flag, converted my backend to https
, added credentials: 'include'
to my front end, enabled CORS (with credentials support), etc., but my back end still doesn't receive a cookie. I've included the absolute bare minimum reproducible example below:
Here's my back end. I can confirm that going to the root URL directly under https
works flawlessly (no warnings). The cert was generated using mkcert.
from flask import Flask, request, jsonify
import jwt
from flask_cors import CORS
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your_secret_key'
CORS(app, supports_credentials=True)
@app.route('/api/user', methods=['GET'])
def get_user():
token = request.cookies.get('auth')
if not token:
return jsonify({'message': 'Token is missing!'}), 403
try:
data = jwt.decode(token, app.config['SECRET_KEY'], algorithms=["HS256"])
username = data.get('username')
email = data.get('email')
return jsonify({'username': username, 'email': email})
except jwt.ExpiredSignatureError:
return jsonify({'message': 'Token has expired!'}), 403
except jwt.InvalidTokenError:
return jsonify({'message': 'Invalid token!'}), 403
@app.route('/')
def index():
return '<html><body><h1>Hello, World!</h1></body></html>'
@app.route('/api/login', methods=['POST'])
def login():
data = request.get_json()
username = data.get('username')
email = data.get('email')
if not username or not email:
return jsonify({'message': 'Username and email are required!'}), 400
token = jwt.encode({'username': username, 'email': email}, app.config['SECRET_KEY'], algorithm="HS256")
response = jsonify({'message': 'Login successful!'})
response.set_cookie('auth', token, httponly=True, samesite='None', secure=True)
return response
if __name__ == '__main__':
# app.run(debug=True)
app.run(debug=True, ssl_context=('/home/ashkan/Desktop/spring 25/spring 25 sideprojects/auth-mre-flask/backend/localhost.pem', '/home/ashkan/Desktop/spring 25/spring 25 sideprojects/auth-mre-flask/backend/localhost-key.pem'))
And here's my basic front end, which is one file that I'm simply opening in the browser:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Basic HTML Page</title>
</head>
<body>
<h1>check the console</h1>
<script>
(async () => {
console.log("logging in...")
const loginResponse = await fetch('https://localhost:5000/api/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
"username": "flask-test",
"email": "a@a.com"
})
})
const loginData = await loginResponse.json();
console.log(loginData)
if (!loginResponse.ok) {
console.log('login failed :(')
return
}
console.log("getting user info...")
const userResponse = await fetch('https://localhost:5000/api/user', {
credentials: 'include'
});
const userData = await userResponse.json();
console.log(userData)
if (!userResponse.ok) {
console.log("user data fetch failed :(")
}
})();
</script>
</body>
</html>
I'm making a login
and a user
request. the former sets my auth cookie, and I expect the second one to send my auth cookie to the server and send back "login successful". in my api client (bruno), this works. in my browser, when opening the fornt-end file, it doesn't.
this is the console in my browser:
index.html:14 logging in...
index.html:27 {message: 'Login successful!'}
index.html:34 getting user info...
index.html:36
GET https://localhost:5000/api/user 403 (FORBIDDEN)
(anonymous) @ index.html:36
await in (anonymous)
(anonymous) @ index.html:44
index.html:40 {message: 'Token is missing!'}
index.html:42 user data fetch failed :(
cookie received on login
(from brave devtools):
Name: auth
Value: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Domain: localhost
Path: /
Expires / Max-Age: Session
Size: 185
HttpOnly: True
Secure: True
SameSite: None
Partition Key Site: (nothing)
Cross Site: (nothing)
Priority: Medium
cookie sent on user
(notice how auth
isn't sent):
Name: csrftoken
Value: 6Q8P4108ffbgVCsmF5AVBY5PxCeHp14A...
Domain: localhost
Path: /
Expires / Max-Age: 2025-07-17T20:50:27.576Z
Size: 41
HttpOnly: False
Secure: True
SameSite: None
Partition Key Site: (nothing)
Cross Site: (nothing)
Priority: Medium
I can't make this example any shorter; sorry...
I have struggled with issues like this in the past. I think your Flask server is fine, considering that it works as expected on Bruno. I suggest trying two things:
Check if the cookie is actually saved in the browser. You can check this with document.cookies, or by going to 'Dev Tools > Application > Cookies'. If the cookie is there, the issue is likely on your client side.
If the cookie is present from the previous step, I recommend adding credentials: 'include' to the login request. (Old post, but maybe could work)
Hope this helps. If not, feel free to update me, and I can take another look. It would be really helpful if you could provide more details, such as whether they are on the same domain and if you are using just vanilla JS