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

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.

Related

Django Session Form (save form temporarily)

I have created a page for review form. All users can fill out the form, but only logged in users can submit the form. If users is not logged in, they will be redirected to the login page. After they login, they will be redirected to the profile page.
So the flow will be like this :
User fills out the form > click the submit > redirected to login page > user login and redirected to profile page (at the same time, the form they have filled in is automatically saved)
I want the form they have filled in automatically saved after they login. How to do that?
My idea is to create a session that saves the form temporarily, then save to database after they login. But I'm confused how to write the code
Can anyone explain a bit what a django session is like? and how to write code to handle this problem?
You can try something like,
1 User fills out the form and hits submit
2 in the POST view where you handle the form, use the "**is_authenticated**" function and,
a)if the user is authenticated you handle the form as usual...
b)else set the contents of the form into a session variable in the views and redirect to the login page like,
request.session['review_body'] = request.post.get(the_form_body_field)
3 as per what you've said, after login it goes to profile page and form is submitted...
a)so in views where you serve the profile page, check if the session variable containing form data's exist and has values
b)if yes, directly save the contents from your views and clear the session data

How to get POST parameters in a Django view protected by #login_required?

Django's login_required decorator requires a login before proceeding to any functionality in a view function. If a user is not logged in, they will be redirected through a login screen.
If the HTTP request is a GET, Django sometimes puts the GET destination in a parameter ("next"). Or, Django also has a LOGIN_REDIRECT_URL setting that is a URL a user is forwarded to after login.
If the HTTP request is a POST, that POST data is lost during the login process.
What is the best way to preserve the original URL and POST data for the original POST request, and re-post that data after the user is logged in?
I imagine putting something in a session, but I'd have to intercept the POST data and URL on the way through login_required. The best practice is not obvious to me.

Django Admin use JWT

Using:
Django 1.11
Python 3.6
DRF with JWT in FE
I understand that the Django admin uses a session, and basic authentication.
What I did so far: Replaced the Django Admin authentication signin page with AWS-Cognito:
The user goes to domain/admin/*, redirected to signin in AWS
On successful signin the user is redirected to the redirect_uri, leads to a Django View
In the view I replace the code with tokens
I can't navigate to any Admin page - I am trying to redirect, but that doesn't work since I didn't login() the User
Stuck - I would like to associate the User with the fetched tokens and authenticate with every Admin page request, and when the user logs out delete the tokens
What to do next?
When I use JWT with the Front End application, every request.META has HTTP_AUTHORIZATION, and uses a suitable backend.
I know how to add backends, and potentially leverage the user.backend (I also use Cognito-JWT for other FE portions, so already wrote BE for that)
I need to find a way to replace the Django Admin sessions authentication with the fetched token
Thank you!
EDIT:
If I login() the user, and set it to a model backend that I have already I can navigate to any admin page - but using the session that I created when I logged the user in.
I would like to have the user be set to a new model backend, with authentication that uses a token (from Django backend docs):
class MyBackend:
def authenticate(self, request, token=None):
# Check the token and return a user.
...
How do I make the different Admin pages requests pass the token to the authentication?
Where do I store the token? (I could make a NewUserModel that is 1-1 with the Django User model, and place a token field there)
I am thinking of writing a middleware to capture all requests, and looking into the target URL - if Admin url, add the token to the HTTP_AUTHORIZATION once I fetch the user mentioned in #2 (the user is in every request due to DRF)
EDIT 2
My solution is getting more and more like this stack solution, I would have liked to know if there are any other options, but here is what I did so far:
I made a model that has a 1-1 user field, and a tokens field
As I am fetching/creating the user, I am also saving the tokens on the user's related model from #1 above
I created a middleware that is capturing any request in process_request, and has access to the user. I can see the tokens there as I access the user's related model from #1 above.
I am trying to set the HTTP_AUTHORIZATION header on the request, but cannot do that yet (currently stuck here)
In my backend, I am looking at the incoming request, and trying to fetch the HTTP_AUTHORIZATION - not there yet.
EDIT 3
I ended up just using the Django session as is - once the user authenticates with AWS-Cognito once, it is safe to assume that it is a legitimate User.
Then I just dump the Cognito-JWT, and login() the User.
Note: I am still interested in a solution that would drop the Django session for using the Cognito-JWT, and would love to hear suggestions.

Redirect after login_required loses POST parameters

I have a view that looks something like this simplified example:
#decorators.login_required()
def add_data(request):
some_value = request.POST['some_value']
# do stuff.
If a user is authenticated already, this completes successfully. If the user is not authenticated, this redirects to the login page. After the login page, the POST request is sent back to my add_data view is missing some_value and all other POST data.
Is this expected? Is there a way to access the POST data?
I am using the default django.contrib.auth.views.login. I have a custom login.html.
Is this expected?
Yes.
Is there a way to access the POST data?
Probably not, the anonymous user and logged in user will have separate sessions so that won't work.
You might be able to some how store the data in a GET variable and pass it to the login form, and then inspect that.
I'd suggest its just a case where the user has messed up, and they should check they are logged in before submitting things.

After user login, when back is pressed login.html displayed

I have a login form. When login button is pressed user's homepage is displayed. But pressing browser's back button takes control back to login form.
I am using django development server.
I have tried inserting meta tags to prevent cache and django #no-cache
But it doesnt work out
The default django login page can be viewed when logged in. You could decorate the login view function and redirect the user if already logged in.
response = <the_way_you_create_response> (ie direct_to_template, HttpResponse)
response['Cache-Control'] = 'no-cache, no-store'
return response
and of course you need to check request.user.is_authenticated() in the view (and do the redirect if value is True)
In your login page you need to check if a users session exists and if it does then you should redirect the user to his homepage.
However, this can't be done with a HTML page so you need to use some kinda server side scripting language(PHP, ASP, JSP Etc) for your login page.