Flask: Saving session data manually - python

I have a Flask application that uses an ajax request, and this request should give a true/false response. The user enters an authorization code, and if the authorization code matches what is needed or is already in the session, then the response should be true and the code added to the session and saved. if it's not, then simply return false
#app.route('/auth-ajax', methods=['POST'])
def auth():
result_id = request.form.get('result_id', 'true')
result = load_result(result_id)
if result:
auth = result['auth_hash']
auth_input = request.form.get('auth_input', '')
if (session.get('auths').get(result_id) != auth and auth_input != auth):
return 'false'
#else, save new authorization into session
session['auths'][result_id] = auth
# return true
return 'true'
However, the session isn't saving as I'd hope. This is my first Python app, and so I'm learning as I go. From what I understand, I need to create a response with Flask and not just simply output "true" or "false" - creating a response save the session as it doesn't save on modification. The only response functions I've used is render_template() for views, but I'm not wanting a view but rather a simple true/false (possibly HTTP status responses) to see if authorization was granted. How would I go about doing this?

Found the problem. I simply has to add session.modified = True. So simple.

Related

Enabling CSRF for Django

I have the following python code in my Django views.py, the code takes in a JSON body and send the extracted DATA to another API endpoint, I have simplified the code here.
How do I enable csrf such that it will send the token back to the caller for this method? I am calling this from postman.
#csrf_protect
def validate_booking(request):
if request.method != "POST":
return HttpResponseServerError("Invalid HTTP method")
body = json.loads(request.body)
booking_details = body["booking_details"]
DATA = {
"name": booking_details["name"],
"nric": booking_details["nric"],
"booking_id": booking_details["booking_id"]
}
return HttpResponse(status="200")
This site directs to put this piece of code in my method. But what is "a_template.html"?
https://docs.djangoproject.com/en/4.1/ref/csrf/
#csrf_protect
def my_view(request):
c = {}
# ...
return render(request, "a_template.html", c)
This isn't an easy thing to do as CSRF is 2 steps thing
There is a value that is passed to the client and it is saved to the session on the server.
When a POST request is received, the client shall send this as csrfmiddlewaretoken in the body and the server will check the value against the stored one in the server's session.
So this isn't feasible to be done in APIs as you require session Management which is not of REST API implementations.
Thanks for your reply. I managed to find a solution by doing the following:
Create a new GET method that will generate the session CSRF token using python
Instead of using render which expects a HTML template file, I used JsonResponse(data) to return in JSON format directly
In my postman app which I am making the POST request with the X-CSRFToken in the header, I will first make a GET request to the new method I created in step 1 to retrieve the token and store it as an environment variable
The following is the GET method sample:
from django.http import JsonResponse
def get_csrf_token(request):
csrf_token = csrf(request)['csrf_token']
data = {'csrf_token': csrf_token}
return JsonResponse(data)

React and Django session problem that works on Postman but not in browser

I was wondering what I'm doing wrong. I am trying to implement the most simple session with React frontend and Django backend. I am aware that my methods are insecure and bad but its just a project for university and I need something that works so I should do other stuff that require sessions in my project.
This is how my backend looks for Login and SessionInfo:
#api_view(['POST'])
def login(request):
data = request.data
try:
user = MyUser.objects.get(username=data.get('username'),
password=data.get('password'))
request.session['uuid'] = user.id
request.session.modified = True
except MyUser.DoesNotExist:
return HttpResponse("User does not exist.")
return HttpResponse(request.session['uuid'])
#api_view(['GET'])
def getsession(request):
if request.session.has_key('uuid'):
return HttpResponse(request.session['uuid'])
else:
return HttpResponse(False)
When I am trying to test this with Postman it always work and I get wanted session ID but when I'm trying to do same stuff with react using Axios post method it always return False. I have no clue why? It looks like Django destroys session after calling login function or it doesn't even create it.
This is how my post method looks in React:
function login(){
axios.post('http://127.0.0.1:8000/evidencija/login/',{
username: 'admin',
password: 'admin'
}).then(
(response) =>{
console.info(response.data)
getSession()
},
(error) =>{
console.log(error)
}
)
}
Some browsers (Chrome, for example) provide settings that allow users to continue browsing sessions after closing and re-opening the browser. In some cases, this can interfere with the SESSION_EXPIRE_AT_BROWSER_CLOSE setting and prevent sessions from expiring on browser close. Please be aware of this while testing Django applications which have the SESSION_EXPIRE_AT_BROWSER_CLOSE setting enabled.
Documentation: https://docs.djangoproject.com/en/3.2/topics/http/sessions/#browser-length-vs-persistent-sessions

Authenticate an API call correctly with requests and sessions

I want to call my own API in a custom view I wrote. Normally I use JWT authentication with my API calls. In this specific view though, I'd like to use a different authentication.
I want to enable logged in users to make a successful get call (without a token). Not logged in users should not be able to make that call. I tried this with Basic Authentication and Session Authentication but don't really get it tow work.
Here is my view that makes the API call:
def visualize_buildings(request, id):
passed_id = id
endpoint = 'linktomyendpoint' + str(passed_id)
response = requests.get(endpoint)
building_group_data = response.json()
# print(building_group_data)
if 'buildings' in building_group_data:
building_data = building_group_data['buildings']
context = {'building' : building_data}
return render(request, 'building_group_visualize_api.html', context)
else:
return HttpResponseNotFound("Ups. We are sorry but no Building Group was found with that id")
Here my API view:
class BuildingGroupRetrieveAPIView(RetrieveAPIView):
authentication_classes = [JSONWebTokenAuthentication,
SessionAuthentication, BasicAuthentication]
serializer_class = BuildingGroupSerializer
queryset = BuildingGroup.objects.all()
The view works with if I send a token in the headers. But how can I use Session Authentication with that? I tried getting username and password from the request and then pass it to the API call. But that doesn't work because I can't decode the password from the request (which makes sense).
So I tried to follow this: https://2.python-requests.org/en/master/user/advanced/ but I still can't authenticate my request.
Can anyone point me into the right direction? Help is very much appreciated! Thanks in advance!
Session ids are saved as a cookie on the user's device and they will be sent to the server as a header name Cookie. So if you want to use cookies instead of the JWT token then you should send your request with the session id as a cookie header.
This is the header that lets Django know your session-id when you visit the site directly:
Cookie: csrftoken=some-csrf-token; sessionid=your-session-id
Now to make your request contain something like that:
cookies = {'sessionid': 'your-session-id'}
response = requests.get(endpoint, cookies=cookies)
Note that Django might still through an error for csrf token based on your settings.
You can find your session-id on your browser. If you don't know where and how to access them, just google it. it's different based on the browser you use.

Is session.modified = True in flask redundant now?

As Flask API documentation says, I need to add
session.modified = True
after my code in order to propagate session changes to mutable structures like dict/list.
As I understand, session object won't see any changes and won't add modified session data to cookies.
Example#1 from docs:
session['objects'].append(42)
# so mark it as modified yourself
session.modified = True
Example#2 from google:
# Add a logout handler.
#app.route('/logout')
def logout():
# Delete the user's profile and the credentials stored by oauth2.
del session['profile']
session.modified = True
oauth2.storage.delete()
return redirect(request.referrer or '/')
But I tried Flask 1.0.2 and Flask 0.10.1. Everything also works without session.modified = True between requests. I see changes, cookies are updated.
Is that line redundant now?

Redirect in flask does not actually redirect to the desired page.

class XX(MethodView):
def get(self):
....
def post(self):
error = None
user = request.form['username']
password = request.form['password']
print user,password
if user == 'xxx' and password == 'xx':
session['logged_in'] = True
session['session_user'] = 'xx'
return redirect("control")
else:
errors = []
errors.append("Login error")
return render_template("login.html" , errors=errors)
#adding rule for control Blueprint
control.add_url_rule("/control",view_func= ControlView.as_view('control'))
The code snippet checks for specific username,password and should redirect to a specific page.
When requested for the login page, a GET request is sent.The above snippet gets username and password and adds to session dictionary.The redirect method results in a POST request and response code is 302 FOUND followed by a GET request to desired page with 200 OK response code.
But the redirection does not happen, it remains on same login page.
what should redirect have as parameters?
redirect('control')
redirect(url_for('control'))
redirect(url_for('control.control'))
Why using render_template('XX.html') responds with 500 response code ??
Is this code inside of a blueprint named 'control'? I'm guessing yes, based on the code shown above.
When you're using blueprints, you cannot know the final URL (eg: /control) in advance, since the blueprint user can map the base URL anywhere they'd like.
The best way to redirect to your view, assuming this is a blueprint, is using the blueprint URL notation: redirect(url_for('control.control')).
If this is NOT going to be used in a blueprint, you should be able to do something like: redirect(url_for('control')) so long as that's your view name when added to the route.
Hope this was clear.
If you're still having errors when trying to two above examples, enable Flask debugging (app.config['DEBUG'] = True) and re-try. The traceback should explain what's happening in more depth.

Categories