Renew CSRF_TOKEN - django

I have an application and I want the user to be able to change the password, while staying logged in.
I'm doing this:
request.user.set_password(password)
request.user.save()
request.user.backend='django.contrib.auth.backends.ModelBackend'
auth_login(request, request.user)
It changes the password and I log in again to keep the user session. But apparently if I do this the CSRF_TOKEN which I have saved in my js client as a variable is no longer valid and I can't use it for POST requests.
Is there a way to renew the CSRF_TOKEN and send it to the client?

From the view, you can get the token with this:
from django.core.context_processors import csrf
print unicode(csrf(request)['csrf_token'])
As far as how to update your js client I would need more details about it before giving good advice. I imagine it would be as simple as returning the new token from the login request. I imagine your login is ajax so you could return the new token in the response and write some JS to update your stored token:
import json
return HttpResponse(json.dumps({"csrf_token": unicode(csrf(request)['csrf_token'])), content_type="application/json")

Related

Jsonresponse in Django working in browser but not in PostMan or Angular

I am trying to send a JSON response from Django back-end to my angular front-end.
When I make the request I receive nothing in Postman or Angular but,opening the link in browser seems to be returning the correct result
My View is :
#api_view(['GET'])
def my_view(request):
print(request.user.username)
return JsonResponse({'username': request.user.username})
When I open http://127.0.0.1:8000/accounts/get_username/ in browser I receive
{"username": "aditya8010"} on the web page.
But when i do a get request using POSTMAN I recieve
{
"username": ""
}
Same with Angular
this.http.get("http://127.0.0.1:8000/accounts/get_username/").subscribe((res) => {
this.username = JSON.stringify(res["username"])
console.log(this.username," ", res)
})
this code also prints an empty username string.
Another thing I have noticed is that my print statement in the view does print anything random I put in there when called from POSTMAN or Browser but when I use request.user.username it doesnt print anything when called by POSTMAN.
And each time the response code is 200
What am I doing wrong.
When you're sending the request you are not providing authentication credentials (i.e. something that identifies the user that is sending the request). How do you obtain this credentials?
You need to establish an authentication method. There are several but I recommend using Token authentication with knox package. Basically, you have an endpoint that logins the user with his username and password (normal json post request) and that endpoint returns a token. This token is what identifies the user. You send this token in the header of each request you need to be authenticated. That means you probably should include an IsAuthenticated permission for the view. In postman:
API view:
from rest_framework.permissions import IsAuthenticated
#api_view(['GET'])
#authentication_classes([IsAuthenticated])
def my_view(request):
print(request.user.username)
return JsonResponse({'username': request.user.username})
When it is in a browser, your login information is remembered in the session. When using postman or Angular, you need to provide the user's information in the request header manually.

flask-login reusable cookies

I am using Flask-login with remember=False (the only cookie is the session cookie). When copy-pasting the session cookie after logging out, for some reason the session is still valid and the user is logged in. Even though the logged out session was deleted properly in the flask logout_user() function - meaning that the ["user_id"] was deleted from the session dictionary. It seems like the session is restored from the old cookie. can someone explain?
I do not really have a right answer for this yet, as I am investigating it myself, but there are a couple of points I would like to make here:
the logout_user() from Flask-login does not really seem to be invalidating the session. It just changes the 'session' cookie's value in the client (the browser). While in the backend this session is still alive.
An experiment to prove this would be: (a simple browser plugin like CookieManager can be used to perform this exercise)
login to the app
take a note of the 'session' cookie's value post successful login
now logout
now observer the 'session' cookie's value again. And you would
notice that it has now changed.
Replace this value with the 'session'cookie's value previously noted
in step 1 above.
Try visiting an internal authenticated page again.
Result : You would successfully be able to view an internal page without re-logging in, proving that the logout_user() never really invalidated the session but just changed the 'session' cookie in the client.
Howeverm, I am myself still taking a look into flask-login logout_user() definition and trying to make sense of it.
I had This issue too. After diagnosing what i found is the decorator #login_required *does not invalidate the User in server side after logout*, which is a security threat. This will be a cake walk for a Hacker to hack your application since they can easily extract all the request and header data from developer tool of your browser and can again send request to you server from outside of the application.For ex: If you have used any API in your application the it will be very easy for Hacker to get all the request data and resend a request using POSTMAN.
I solved this issue by creating a separate decorator "#authentication_required" and used in place of "#login_required". then it worked for me,though #login_required is supposed to do the same.
So basically while logging in i generated a random string(token) and sent to database and same string(token) is added to session of flask i.e session["token"]="akhfkjdbfnd334fndf" use any random string generator function.(session object is globally available if u r using flask . u can very well add any field to session). and while logout i again generate a string(token) and update the old token with newly generated token in database. So what #authentication_required will do is it will get the token from session object and the token which is present in database and try to compare the value. if both are same then only #authentication_required will let the client access api.and dont forget to do session.clear() after logout_user().
#---------------------------------------------------------------#
##authentication_required definition
def authentication_required(f):
#wraps(f)
def wrap(*args, **kwargs):
try:
user_id=session['user_id'] #assigning "user_id" from flask session object to a variable "user_id"
user=User_table.find_first(_id=user_id)#couhdb query syntax
#comparing api_token sent to session object at the login time with api_token in sent to database at login time. If both doesn't matches then user is redirected to login page.
if not session["token"]==user.token:
return redirect(url_for('login'))
else:
return f(*args, **kwargs)
except:
app.logger.info(Response('Request Did not came through header !', 401, {'WWW-Authenticate': 'Login failed'}))
return redirect(url_for('login_to system'))
return wrap
#---------------------------------------------------------------#
-------------------------------------------------------
login api code
#app.route('/login_to_system', methods=['GET', 'POST'])
def login_to_system():
form = LoginForm()
user = User_table.find_first(username=form.username.data)
login_user(user, remember=False)
try:
#Generating an random api_token at login time and will send to db
token_string=''.join(random.choices(string.ascii_uppercase + string.digits, k=14))
user.token=token_string #assigning token_string value to field api_token in database.
user.save() #saving the value in user table(using couch Db You can follow syntax as per you DB)
except Exception as error:
app.logger.error(str(error))
app.logger.info("before setting api_token in session")
session["token"]= token_string #adding a "token" to session object
#app.logger.info("Rendering login form")
return render_template('login.html', title='Sign In', form=form)
#-------------------------------------------------------#
#-----------------------------------#
#logout api code
#app.route('/logout')
def logout():
try:
user=User_table.find_first(_id=user_id)
#Generating a random token while logging out and will overwrite eariler token sent at login time send to database.
user.token=token_string=''.join(random.choices(string.ascii_uppercase + string.digits, k=17))
user.save()
except Exception as error:
app.logger.error(str(error))
logout_user()
session.clear()#clearing session
return redirect(url_for('Home page'))
#-----------------------------------#
Note: Seems like login_required is not working fine for me thats why i had to create another decorator but login_required also does the same thing but its strange that it not working for me.

django tests response.request.user.is_authenticated() returning True after logout

I am trying to write some tests for the authentication part of my application and I encountered a problem with checking if the user is logged in or not. Here's the code:
client = Client()
# user signup form
response = client.post(signup_url, data={
'email': "lorem#ipsum.pl",
'password1': 'hunter2',
'password2': 'hunter2',
}, follow=True)
# checking if the user is logged in
with self.assertRaises(KeyError):
client.session['_auth_user_id']
self.assertEquals(len(mail.outbox), 1)
url = find_verification_url(mail.outbox[0].body)
response = client.get(url, follow=True)
self.assertEqual(200, response.status_code)
user = User.objects.get(email="lorem#ipsum.pl")
self.assertEqual(client.session['_auth_user_id'], user.pk)
# how to logout a user?
force_logout()
self.assertFalse(response.request.user.is_authenticated())
The user fills the form and clicks submit, then receives an email with a verification url. After he clicks the verification url in the email he's supposed to get directed to the site and authenticated. My questions is, what is a good way to find out if the user is authenticated or not? What is a preferred way to log out a user in this situation? I want to check that if the user is logged out and clicks the link the verification link second time it doesn't work. I tried some things like:
client.logout()
But unfortunately it seems to work once every two times even when I remove this line.
Thanks for any help!
Ok so the problem was that the authentication system was using a timestamp function to know if a url was expired or not. When run in a test the verification url was not expired when it should be. The login request after the logout was too fast and the system was thinking that the verification url was still valid and the user got authenticated. And that's why user.is_authenticated() was returning True all the time.

Does the CSRF info change when logging in via ajax request?

I have a form with csrf token that works.
There is also a button to upload a picture via ajax and put the url into a textarea of the first form on the same page. I have some js inplace to set the csrf value and the button also works fine.
If the user is logged in there is absolutely no problem when using first the button to insert the image url and then saving the form.
But when I have a visitor who is not logged in there is a problem: When he uploads a picture a new account is automatically created and the visitor is logged in without him doing anything.
while True:
try:
random_password = User.objects.make_random_password()
random_username = str(uuid.uuid1().hex)[:5]
new_temp_user = User.objects.create_user(random_username, password=random_password)
except IntegrityError:
pass
else:
new_user = authenticate(username=random_username,password=random_password)
login(request,new_user)
break
Since this is done via Ajax he doesn't notice any change to the website he is seeing. Only the url of the uploaded picture gets added to the first form. On the server side he is now a registered user, but he doesn't see it yet. Now when he submits the form the csrf validation fails. The form still has the csrf token in place, but somehow it became invalid.
I suspect that the login process has some influence over the csrf token. Any ideas what I may do about it?
EDIT: I checked some more and it seems that the problem is due to the login. Every login seems to change the csrf value. Now when the user gets a login over ajax the token in the form doesn't update. What may be the best way to update it?
The csrf token is rotated when the user logs in. This was added as a security measure in Django 1.5.2 (release notes).
After the ajax request, you might be able to fetch the new csrf token from the cookie, and update the form in the DOM. Alternatively, you could refresh the page after the user is logged in.

django does not send csrf token again after browser cookies has been cleared

In normal situation, django will send csrf token via cookie which can be used by ajax post method later. However when I clear cookies in the browser(Chrome or Firefox), the csrf token is not sent to browser anymore, the session id is still sending but no csrf token. Does anyone know what's going wrong?
I solved this issue by adding {% csrf_token %} to my template and the SET-COOKIE header appears along with that page request. it turns out that you have to put the {%csrf-token%} in the template in order to make the server send the token via SET-COOKIE header
Look at django/middleware/csrf.py in which CsrfViewMiddleware class is declared. As you can see in def process_response(self, request, response) there are three conditions that prevent cookie setup:
def process_response(self, request, response):
if getattr(response, 'csrf_processing_done', False):
return response
# If CSRF_COOKIE is unset, then CsrfViewMiddleware.process_view was
# never called, probaby because a request middleware returned a response
# (for example, contrib.auth redirecting to a login page).
if request.META.get("CSRF_COOKIE") is None:
return response
if not request.META.get("CSRF_COOKIE_USED", False):
return response
# Set the CSRF cookie even if it's already set, so we renew
# the expiry timer.
response.set_cookie(settings.CSRF_COOKIE_NAME,
request.META["CSRF_COOKIE"],
max_age = 60 * 60 * 24 * 7 * 52,
domain=settings.CSRF_COOKIE_DOMAIN,
path=settings.CSRF_COOKIE_PATH,
secure=settings.CSRF_COOKIE_SECURE
)
# Content varies with the CSRF cookie, so set the Vary header.
patch_vary_headers(response, ('Cookie',))
response.csrf_processing_done = True
return response
Check which is applied for you.
I had the same problem. After debugging against django source, the reason is:
If your view is not rendering a template containing the csrf_token
template tag, Django might not set the CSRF token cookie.
Two solutions:
Add {% csrf_token %} in your template
Use #ensure_csrf_cookie decorator for your view
For detail your can refer django doc.
In my case, the problem was VSCode debugger.
I turned on server via VSCode debug mode, then open new incognito window (obviously there were no cookies), and django stopped setting missing cookie.
When I started server as normal, the problem has gone.
In most cases issue caused by second check mentioned in a previous answer
if not request.META.get("CSRF_COOKIE_USED", False):
return response
This can be solved by using #ensure_csrf_cookie decorator for the view. If used - check is passed and cookie is set/renewed each time view is rendered.
See also related topic: Using ajax request in Django without form element