Couldn't set cookie with flask - python

For development I am using vuejs which is being served by webpack at my local address: 172.18.0.77:8080 and flask that is run by Werkzeug at address 172.18.0.77:5000. I am trying to set cookie to some GET request by running this code:
response_data = Response(json.dumps(some_json_data, indent=True), status=200, mimetype='application/json')
response_data.set_cookie('user_session_id', value='12345', domain='172.18.0.77:8080')
return response_data
But when I am trying to read this cookie with following code request.cookies.get('user_session_id') I am receiving only None value.
I also tried to set cookie by changing domain to 172.18.0.77 like:
response_data.set_cookie('user_session_id', value='12345', domain='172.18.0.77')
But it also doesn't work

if you use axios in vuejs, i suggest that you can add withCredentials: true
const instance = axios.create({
withCredentials: true,
....
})
And in flask
#app.after_request
def handle_credentials(response):
response.headers["Access-Control-Allow-Credentials"] = True
return response

Related

Sending cookies/sessions to subdomains

As the title says, I'm trying to set cookies/sessions on the browser.
To preface:
Frontend: https://www.example.com
Backend: https://api.example.com
Frontend makes a request to the backend, the backend creates a session, and a cookie is sent back to the frontend in the response.
Problem:
The request is successful and the response shows that there is a session, however it was not set. The error message is as follows:
This attempt to set a cookie via a Set-Cookie header was blocked because its Domain attribute was invalid with regards to the current host url.
Research:
Based off this and this, it appears that:
cookies can be shared is that both domains have the same main-domain
Which is the case for my scenario. The main domain is example.com and the subdomains are www.example.com and api.example.com.
In addition, Mozilla official documentation states that:
if a domain is specified, then subdomains are always included
Implementation
The frontend is developed in React and the backend is developed in Python (Flask). Both are hosted Heroku. Domain registrar is with GoDaddy and I'm using Cloudflare as my DNS to re-route traffic and setting the correct domains.
A sample request looks like this:
const headers = {
'Content-Type': 'application/json',
'X-CSRF-Token': csrfToken
};
...
const handleFormSubmission = async e => {
e.preventDefault();
await axios.post('https://backend.com/add-results', { tokenId: tokenId }, { withCredentials: true, headers: headers })
}
And the backend has the following settings:
...
CORS(app, origins=["https://www.example.com"], expose_headers=["Content-Type", "X-CSRFToken"], supports_credentials=True)
...
app.config['SESSION_COOKIE_SECURE'] = True
app.config['SESSION_COOKIE_HTTPONLY'] = True
app.config['REMEMBER_COOKIE_SECURE'] = True
app.config['REMEMBER_COOKIE_HTTPONLY'] = True
app.config['SESSION_COOKIE_DOMAIN'] = 'example.com'
Was wondering if anyone has experienced a similar problem and found a solution. I got the idea of using subdomains from here, but I have had no luck with my implementation.
Here's a screen shot of what I'm getting
The fix was actually pretty simple:
CORS(app, origins=["https://www.example.com", "https://example.com"], expose_headers=["Content-Type", "X-CSRFToken"], supports_credentials=True)
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY')
app.config['SESSION_COOKIE_SECURE'] = True
app.config['SESSION_COOKIE_HTTPONLY'] = True
app.config['REMEMBER_COOKIE_SECURE'] = True
app.config['REMEMBER_COOKIE_HTTPONLY'] = True
app.config['SESSION_COOKIE_SAMESITE'] = 'None'
app.config['SESSION_COOKIE_DOMAIN'] = '.example.com'
app.config['WTF_CSRF_SSL_STRICT'] = False
Main changes were:
app.config['SESSION_COOKIE_SAMESITE'] = 'None'
And:
app.config['WTF_CSRF_SSL_STRICT'] = False
SESSION_COOKIE_SAMESITE needed to be 'None' to avoid the default value of 'Lax' which would prevent the cookie from being received and set on the browser.
WTF_CSRF_SSL_STRICT was set to false because the origins of the request and the server needed to be the same (same origin policy). However, I have configured a whitelist of allowed origins in my CORS configuration. That said, I think that covers that case.
Also, I cleared my caches on my browser and the DNS. In addition, I restarted my servers where the frontend and backend were hosted.
Hopes this helps others, cheers!

How to send data from localhost to server using Python requests?

I am trying to send data from localhost to an API in remote server using Python Requests & Django through an API: /api/send-data/
Path for /api/send-data/ in urls.py is path('send-data/',view_send_data.send_data,name='send_data')
This is my view_send_data.py in my localhost:
#api_view(('POST',))
#renderer_classes((TemplateHTMLRenderer, JSONRenderer))
def send_data():
# defining the API-endpoint
API_ENDPOINT = "http://68.183.89.234/api/screen/"
# data to be sent to API
data = {'data':1234}
# sending post request and saving response as response object
r = requests.post(url = API_ENDPOINT, data = data)
print(r.json())
return Response({"success": True}, status=status.HTTP_200_OK)
Path for api at http://68.183.89.234/api/screen/ in urls.py is path('screen/',views_fr.screen_display,name='screen')
This is my views_fr.py in my remote server:
#api_view(['POST'])
def screen_display(request):
if request.method == 'POST':
return Response({"success": True,
"response": request.data}, status=status.HTTP_200_OK)
When I call "http://127.0.0.1:8000/api/send-data/" in my browser, I am getting 405 Method Not Allowed
Is this the correct way to do it or am I missing something? Thank you.
The error you are getting is because the remote server API only accepts POST method, but you can't make a POST request using the browser url call. Test the API using postman or curl so you can set the correct method for request.

Using return vales from flask in ios application

I was trying to use flask as a backend for my iOs application. Currently it seems to be working, and the backend is hosted on heroku. The flask backend looks a little like this:
#app.route('/get_token', methods=['POST'])
def create_token():
token = make_token()
return token
I can run this function and confirm that it runs using a snippet like this with swift (using alamofire):
let url = "https://my-backend.herokuapp.com/get_token"
Alamofire.request(url, method: .post, parameters: nil, encoding: JSONEncoding.default)
And that runs fine. But now I want to do something (specifically save the token from flask) with the return value from flask. But I am confused as to how to do this. Any suggestions?
I would return a JSON response from Flask, and then you can easily parse that JSON object however you choose in your iOS app. Flask has a built in method, jsonify, which makes it easy to create a JSON responses.
You response would look like return jsonify(token=token)
Parse JSON with Alamofire:
Alamofire.request(url, method: .post, parameters: parameters, encoding: JSONEncoding.default)
.responseJSON { response in
print(response)
//to get status code
if let status = response.response?.statusCode {
switch(status){
case 201:
print("example success")
default:
print("error with response status: \(status)")
}
}
//to get JSON return value
if let result = response.result.value {
let JSON = result as! NSDictionary
print(JSON)
}
}
Source: https://stackoverflow.com/a/33022923/6685140

Flask session not persisting between requests for Angular App

I have an Angular app that needs to call a Flask server that use sessions to store information between requests.
I also have an older JS app that called the same server using XMLHttpRequest, that I am replacing with the new Angular app.
The trouble is that when the old app was making a request, session cookies were working as expected but now with the angular app it does not.
All interactions are done over localhost. The Flask server is accessible from localhost:5000, and the Angular app from localhost:4200.
The old app was doing request like this:
var xhttp = new XMLHttpRequest();
xhttp.open("GET", "http://localhost:5000/api/getAll", true);
xhttp.withCredentials = true;
xhttp.send();
The Angular app is doing like so:
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders, } from '#angular/common/http';
import { Observable } from 'rxjs';
const httpOptions = {
withCredentials: true,
headers: new HttpHeaders({
'Content-Type': 'application/json',
'charset': 'UTF-8',
})
};
#Injectable()
export class ServerService {
url = "http://localhost:5000/api/"
constructor(private http:HttpClient) { }
getAll(): Observable<string>{
return this.http.get<string>(this.url + 'getAll', httpOptions);
}
login (username: string): Observable<string> {
return this.http.post<string>(this.url + 'login', JSON.stringify({"username": username}), httpOptions)
}
}
And the Flask server:
from flask import Flask, session, request, jsonify
from flask_cors import CORS
import os
import Person
import multiprocessing as mp
import json
import Insurance
import datetime
import Functions
import missingVal
app = Flask(__name__)
CORS(app, supports_credentials=True)
# set the secret key. keep this really secret:
# The value come from calling os.urandom(24)
# See https://stackoverflow.com/a/18709356/3729797 for more information
# app.secret_key = b'fL\xabV\x85\x11\x90\x81\x84\xe0\xa7\xf1\xc7\xd5\xf6\xec\x8f\xd1\xc0\xa4\xee)z\xf0'
app.config['SECRET_KEY'] = b'fL\xabV\x85\x11\x90\x81\x84\xe0\xa7\xf1\xc7\xd5\xf6\xec\x8f\xd1\xc0\xa4\xee)z\xf0'
#app.route('/api/getAll')
def getAll():
response = jsonify()
if 'username' in session:
user = users[session['username']]
# some more logic here
response = jsonify({'username': session['username']})
return response
# login and account creation
#app.route('/api/login', methods=['POST'])
def login():
response = jsonify()
if users.get(request.json.get('username')) is not None:
session['username'] = request.json.get('username')
# some more logic here
response = jsonify({'username': session['username']})
response.headers.add('Access-Control-Allow-Methods',
'GET, POST, OPTIONS, PUT, PATCH, DELETE')
response.headers.add('Access-Control-Allow-Headers',
"Origin, X-Requested-With, Content-Type, Accept, x-auth")
return response
if __name__ == '__main__':
# some more logic here
app.run(host='localhost', threaded=True
The problem is that when I log in, it push information into the session, and when I do another request, I check if that information is in the session but it does not.
I found a lot of other related question on StackOverflow:
this one has to do with setting the secret_key multiple times, which is not my problem.
this one talk about static versus dynamic configuration in init but I don't think it has anything to do with my problem? Tell me if I'm wrong.
this one and this other one had trouble because their payload inside the cookie was too big, only 4096bytes or less are allowed it seems. But I only put a username that is a few letters into my cookie so I don't believe it is my problem.
this one I thought was related to my problem because it deals with localhost, but it turns out that it was because OP was mixing requests on 127.0.0.1 and localhost and cookies are processed separately by flask apparently. I do all my request on localhost so not related I believe.
I'm a bit lost right now, there is probably something very obvious I am missing but can't figure it out, any suggestion appreciated
I got it working by adding
response.headers.add('Access-Control-Allow-Headers',
"Origin, X-Requested-With, Content-Type, Accept, x-auth")
in the Flask server before sending back all requests.
For example
#app.route('/api/doSomething', methods=['POST'])
def doSomething():
response = jsonify()
if 'username' in session:
# some logic here
response = jsonify(someData)
# here is the line I added
response.headers.add('Access-Control-Allow-Headers',
"Origin, X-Requested-With, Content-Type, Accept, x-auth")
return response
Apparently it is needed when doing CORS, some good informations on MDN

Making an Angular2 POST with JSON

I have a simple Flask backend for the API, and want to make a POST.
I'm not sure the error is-I get the error message, but am unsure if its an Angular or Flask issue. I tried request.get_json() in my Flask, and got
{BadRequest}400 BadRequest: The browser (or proxy) sent a request that
this server could not understand
Angular2 call in my service.ts:
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Observable} from 'rxjs/Rx';
import { Headers } from '#angular/http';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
#Injectable()
export class NicksService {
private headers = new Headers({'Content-Type': 'application/json'});
nickUrl: string = "http://localhost:5000/nick";
constructor(private http: Http) { }
// Send the nick to the server to see if it is valid
checkNickValidity(nickName: string): Observable<string>{
return this.http.post(this.nickUrl, JSON.stringify({nick: nickName}), {headers: this.headers})
.map(this.extractData)
.catch(this.handleError)
}
Flask backend:
from flask_cors import CORS
from flask.ext.api import FlaskAPI
from flask import request
app = FlaskAPI(__name__)
CORS(app)
#app.route('/')
#app.route('/nick', methods=['POST'])
def check_nick():
try:
nick = request.json
if nick =='AmandaPanda':
return 'Good'
else:
return 'No nick found'
except:
return 'error'
if __name__ == '__main__':
app.run()
I think you have problems on both sides of your application.
Client side:
Angular's Http's post method receives any object in for the body parameter, meaning you don't need to stringify it.
E.g.:
this.http.post(this.nickUrl, {nick: nickName}, {headers: this.headers})
Server side:
Once the client's request arrived properly to the server, you are not getting the data properly.
What you need to do is something like this:
from flask import jsonify
(... your code ...)
data = request.json
nick = data.get('nick')
if nick == 'AmandaPanda':
return jsonify({'message': 'good'}), 200
else:
return jsonify({'message': 'no nick found'}), 400
Here, the json data is stored as a dictionary in the variable data. Later, the nick is obtained from the nick key of said dictionary.
Finally, it is a better practice to return to the client in json form. That's what Flask's jsonify method is for. Notice the 200 and 400 status codes appended to the response, to indicate to the client if the request was properly accepted (200) or if it was a bad request (400).

Categories