How to render Flask-Security forgot password form? - flask

I have application with ready UI and I want to add login/logout/register/restore features with Flask-Security. Before I worked with that default behavior - when user clicked "forgot password" he was redirected to specific endpoint.
Now I want to have forgot password form on the same page (just in different panel which show when user clicks corresponding link).
I faced an issue that I cannot just add the same form with same endpoint because Flask-Security wants CSRF token. I think that I can somehow render its form on page and adjust styles. But I do not know how.
I do not want to turn off csrf check unless I definitely know that there is not other ways.

Since you are generating the form dynamically I will assume that you are using AJAX, the documentation speaks about it.
You have to enable the CSRF module with
from flask_wtf.csrf import CsrfProtect
CsrfProtect(app)
you will have access to csrf_token() on every page, and you can get it with:
<meta name="csrf-token" content="{{ csrf_token() }}">
var csrftoken = $('meta[name=csrf-token]').attr('content')
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken)
}
}
})

Related

django CSRF token cookie not set for some users

I have been getting sporadic CSRF errors in an app that is mostly working ok. I do everything as I'm supposed to do: I use {% csrf_token %} in my template for normal forms and in my ajax POSTs I set the X-CSRFToken header:
$.ajaxSetup({
beforeSend: function(xhr, settings) {
xhr.setRequestHeader("X-CSRFToken", $.cookie('csrftoken'));
},
});
I'm even forcing the cookie to be set in all views by writing a custom Middleware that calls get_token
def CSRFForceCookieMiddleware(get_response):
def middleware(request):
response = get_response(request)
get_token(request) # Force to set cookie in all responses
return response
return middleware
Everything works OK in my localhost and in production for most users. But for some users I get 403 CSRF validation error.
I added a lot of debug info. Turns out that even if CsrfViewMiddleware is setting the csrftoken is setting the cookie in the response, in the actual browser the cookie is not set ($.cookie('csrftoken') is null). So when the ajax call is made, there is no cookie present in the request.
So, I guess this pretty much means that some users' browsers are blocking this cookie? Anyone else had this experience?
Most browsers have an option to "block all cookies". You may want to detect that in javascript and give your users a warning that some functional cookies are required for the site to work correctly. There's another SO question that shows how to do that.
Alternatively, grab the token from a hidden input field ({% csrf_token %} will add that field in your template). That should always work.

Django CSRF failure, using React forms

I'm having a problem with CSRF with Django and React.
I have read through the already high number of questions around this, as well as the django docs naturally. I have tried every possible combination of different things that should address the issue but am still struggling with it.
Firstly I tried to create a register page, but when I POST to register/ I get CSRF cookie not set, 403.
I have gone so far as disabling the CSRF middleware [bad I know, just trying to get somewhere] and I am getting 405s, method not allowed [attempting to post]. I just thought maybe this is something someone has run into before or sounds familiar and could give some guidance?
I have tried:
- adding the decorator #csrf_exempt,
- adding the CSRF to the header of a request,
- attaching the whole cookie,
- attaching a hidden form field with the token.
I am using this seed project: https://github.com/Seedstars/django-react-redux-base if anyone wants to have a look, I've done a bit in React, but not a lot on the Django side, so it isn't far off what's there
You should not disable the csrf check in django.
Instead in your form/template simply do
{% csrf_token %} not {{ csrf_token }}
It will print a hidden form element with value assigned to your csrf token already.
If you are using ajax, you can simply set your ajax headers globally as:
$.ajaxSetup({
beforeSend: function (xhr, settings) {
// this time double brackets
xhr.setRequestHeader("X-CSRFToken", "{{csrf_token}}");
}
});
if you are using fetch then:
fetch('some/url/here', {
method: 'GET',
headers: {
'X-CSRFToken': window.CSRF_TOKEN // or pass it in your own way
}
}).then(function (response) {
return response.json()
})
These are pretty much all the ones i can think of.
Hope this helps.

403s resubmitting a form after login

I get a 403 under the following repro steps:
While logged out, try to submit a Django form that generates a validation error
Log in or signup for a valid account
Using the browser, go BACK to the page with the validation error
Resubmit the form
Results: 403 error. This is most likely expected behavior, however I'm looking for a more graceful way to handle this. Is there a good way to catch this and resubmit the form as the logged in user?
I have seen this question asked in the context of many frameworks, and the only elegant solution is JavaScript.
With JavaScript, you could store the input values in localStorage. Then on successful form submit event, clear those values. If the form is loaded with those values existing in localStorage (the form submission returned 403, and the user went back to the form page), then automatically populate the form with the values.
Its not really that complex to implement, just more work. I believe there are JS libraries based on this idea...
Give all your form elements a classname. In the example I will use store-data. This can be set in forms.Widget.attrs if you define your form in django, or just with the class attribute on input elements if you write your own html.
On submit, add an item named formData to localStorage. formData is a JS object mapping form field element ids with the classname from above to the element values.
If the form is submitted and processed as valid, on the redirect page remove formData from localStorage with localStorage.removeItem().
When loading the form page (this would be where the user went back to the form after a 403), if formData exists in localStorage then load the values into the form fields.
Here is an example form with this implemented:
<form name="myForm" action="{% url 'myapp:form_submit' %}" onsubmit="return storeData()">
<label>Name: </label>
<input type="text" class="store-data" id="inputName" />
<label>Description: </label>
<textarea class="store-data" id="textareaDescription"></textarea>
<input type="submit" />
</form>
<script>
function storeData() {
var elements = document.getElementsByClassName("store-data");
var formData = {};
// store element ids and values in formData obj
for (var i = 0; i < elements.length; i++) {
formData[elements[i].id] = elements[i].value;
}
// store formData to localStorage as string
localStorage.setItem('formData', JSON.stringify(formData));
}
// if the localStorage item has already been set, then the user tried to submit and failed
if (localStorage.getItem('formData')) {
formData = JSON.parse(localStorage.getItem('formData'))
// set all the form elements to the values that were stored when the user tried to submit
for (var key in formData) {
document.getElementById(key).value = formData[key];
}
}
</script>
And on the redirected success page, be sure to remove the formData item. Otherwise, any time the user goes back to the form the values will be loaded into the fields. (I suppose this may be a desired behavior, but I doubt it.)
<script>
localStorage.removeItem('formData');
</script>
Well, yes, it's expected behaviour. When you login, new csrf_token is generated. And when you navigate back to page with validation error, it still contains old csrf_token in <input type="hidden" name="csrfmiddlewaretoken" value="old_token" />. So you submit form with invalid csrf_token and get 403 error.
I can suggest two options for you (none of them I like)
Disable new csrf_token generation on login. Just place request.META['CSRF_COOKIE_USED'] = False after login(request, user) in your loggin view.
Disable csrf protection via decorator for your single view, or globally by removing csrf middleware from your settings.py.

Csrf token verification fails between two Django web applications

I am trying to pass csrf token between two web application to make one POST data to the other.
"client" application (C) asks csrf token to "server" application (S) via a GET operation.
S responds to C with a form:
<form id='csrfRequestForm' name='csrfForm' action='http://{{ context_path }}/ajax/getcsrf' method='post'>
<!-- csrf token -->
{% csrf_token %}
<!-- datas to POST follow -->
...
</form>
C has to submit this form to action (mapped on a url used by S) in order to POST datas to S.
When C tries to do it, csrf verification fails. I've checked GET's result and csrf token is received with the form. I have django.middleware.csrf.CsrfViewMiddleware keyword listed under MIDDLEWARE CLASSES in settings.py and RequestContext is passed when rendering form's view with render_to_response(... RequestContext(request))
What am I doing wrong?
Thanks
Try defining your context and returning it like this...
context = RequestContext(request, {
'request': request
})
return render_to_response(..., context_instance=context)
This is by design, and disallows for cross site POST execution. One option you have is to mark the methods you would like to be able to execute as safe, as per the django docs:
https://docs.djangoproject.com/en/dev/ref/contrib/csrf/
I wasn't able to resolve it in your way, but I managed out how to do it:
C go directly to S via javascript opening a popup with:
window.open("http://<S_address>/<path_to_request_form>");
In this way, user using C that is logged via a third party authentication server (I forgot to mention it earlier, sorry), is still logged in the popup window in S and receives the form in it with a correct csrf token. I don't know if it's correct but it works.
Thanks for your time

form submit jQuery mobile

I've gotten it into my head that mobile applications don't like form submits the same way html does, so I thought I'd better have a sanity check on Stackoverflow.
For example, instead of having <input type="submit"...>, it looks like I should now use <a data-role="button"...>
Q: Can I continue to use <input type="submit"...> for mobile applications?
The reason why I ask is because the action page has some logic, such as:
<cfif structKeyExists(form,"Save")>
jQuery Mobile, at least as of this writing, by default submits forms via AJAX using the method specified on the form being submitted. POST submissions will still be posted to the server in the background, so ColdFusion will still see the form variables that are passed in as usual. When a response is generated, jQuery Mobile will take the response and transition the view over to whatever HTML was returned. In my own testing you can continue to use a normal submit button as well. If you want a standard submission rather than an AJAX submission, add data-ajax="false" to the form tag.
If you want to programatically submit a form, set the data-ajax attribute for the form to false and then set an event handler for the submit event for the form:
<form data-ajax=false></form>
$(function () {
$('form').bind('submit', function (event) {
event.preventDefault();
$.post('path/to/server.file', $(this).serialize(), function (data) {
alert('Server Response: ' + data);
});
});
});