Production only: sometimes get 403 CSRF verification failed - python

I have a login form that logs the users into the admin site. It works fine in development, and mostly works fine in production, but sometimes it gives a 403 CSRF verification failed error. Note that this happens to users that were able to log in before, so I can't imagine it's an issue with their browser.
It looks like jenniwren had a similar issue in this comment. They never asked a question about it, and the other commenters had no clue why that would happen.
Here's what I have:
urls.py
urlpatterns += patterns('django.contrib.auth.views',
url(r'^logout$', 'logout', {'next_page': 'mysite_login'}, name='mysite_logout'),
url(r'^login$', 'login', name='mysite_login'),
url('^', include('django.contrib.auth.urls')),
)
main/registration/login.html
{% extends "base.html" %}
{% load staticfiles %}
{% block content %}
{% if form.errors and not form.non_field_errors %}
<p class="errornote">Please correct the error(s) below.</p>
{% endif %}
{% if form.non_field_errors %}
{% for error in form.non_field_errors %}
<p class="errornote">
{{ error }}
</p>
{% endfor %}
{% endif %}
<form action="{{ app_path }}" method="post" id="login-form">
{% csrf_token %}
<div class="form-row">
{% if form.errors %}
{ form.username.errors }}
{% endif %}
{{ form.username.label_tag }}
{{ form.username }}
</div>
<div class="form-row">
{% if form.errors %}
{{ form.password.errors }}
{% endif %}
{{ form.password.label_tag }}
{{ form.password }}
</div>
<input type="hidden" name="next" value="{{ next }}" />
<div class="submit-row">
<input type="submit" value="Log in" />
</div>
<div class="password-reset-link">
Forgot your password?
</div>
</form>
{% endblock content %}
settings.py
INSTALLED_APPS = (
'filebrowser',
'grappelli',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'psycopg2',
'main',
'mysite'
)
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware'
)
SECURE_CONTENT_TYPE_NOSNIFF = True
SECURE_BROWSER_XSS_FILTER = True
CSRF_COOKIE_HTTPONLY = True

This problem might occur if:
A user opens the login page in two different tabs
A user logins in one tab.
A user tries to login again in a different tab (although he is already logged in).
If your CSRF_FAILURE_VIEW shows your site template, you might be able to let the users know they are already logged in, and do not need to refresh the page.

This is a message I got whed Debug=True:
The form has a valid CSRF token. After logging in in another browser tab or hitting the back button after a login, you may need to reload the page with the form, because the token is rotated after a login.

I think this answer will help you, you can also use #csrf_exempt annotation
over your function(view) like this:
#csrf_exempt
def foo():
return 'bar'
django doc on this subject: https://docs.djangoproject.com/en/3.0/ref/csrf/

I was doing some proxying for the admin url and fixed my issue by editing my settings.py file and adding:
CSRF_TRUSTED_ORIGINS = [".subdomain.com"]
Using my organisation's gsuite subdomain. See the django docs for reference

Related

Using App name with Django.contrib.auth views

I am new to Django and I am creating a user change password page. However, I keep encountering a NoReverseMatch error which I suspect is due to my app name but I am not able to resolve it even after spending hours googling for a solution.
My urls.py file:
from os import name
from django.urls import path
from account import views
from django.contrib.auth import views as auth_views # Import Django built-in authentication views
app_name = 'account'
urlpatterns = [
path('test/', views.test_login, name='test'),
path('login/', auth_views.LoginView.as_view(), name='login'),
path('logout/', auth_views.LogoutView.as_view(), name='logout'),
path('password_change/done/', auth_views.PasswordChangeDoneView.as_view(), name='password_change_done'),
path('password_change/', auth_views.PasswordChangeView.as_view(), name='password_change'),
]
My settings.py:
# Login Logic
LOGIN_REDIRECT_URL = 'account:test' # Tells Django which URL to redirect user to after a successful login if no next parameter is present in the request
LOGIN_URL = 'account:login' # The URL to redirect the user to log in - based on the name in the urls.py
LOGOUT_URL = 'account:logout' # The URL to redirect the user to log out - based on the name in the urls.py
my html file
{% extends "base.html" %}
{# This is the template to allow user to change their password #}
{% block title %}Change Your Password{% endblock title %}
{% block content %}
<div class="container">
<div class="row justify-content-center">
<div class="col-8">
<form action="{% url 'account:password_change_done' %}" class="form-signin" method="post">
{% csrf_token %}
<h1 class="h3 mb-3 font-weight-normal text-center">Change your password</h1>
<div class="form-group">
<label for="old_password">Old Password</label>
<input class="form-control" type="password" required id="id_old_password" name="old_password" autocomplete="current-password" placeholder="Old Password" autofocus>
</div>
<div class="form-group">
<label for="new_password1">New Password</label>
<input class="form-control" type="password" required id="id_new_password1" name="new_password1" autocomplete="new-password" placeholder="New Password">
</div>
<div class="form-group">
<label for="new_password2">Confirm Password</label>
<input class="form-control" type="password" required id="id_new_password2" name="new_password2" autocomplete="new-password" placeholder="Confirm Password">
</div>
<small class="form-text text-muted">
{% if form.new_password1.help_text %}
{{ form.new_password1.help_text|safe }}
{% endif %}
</small>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
{% if form.errors %}
{% for field in form %}
{% for error in field.errors %}
<div class="alert alert-danger">
<strong>{{ error|escape }}</strong>
</div>
{% endfor %}
{% endfor %}
{% for error in form.non_field_errors %}
<div class="alert alert-danger">
<strong>{{ error|escape }}</strong>
</div>
{% endfor %}
{% endif %}
</div>
</div>
</div>
{% endblock content %}
Much help is appreciated.
Have added in the html file for referece
A "NoReverseMatch" error means , Django is not being able to process and see the exact url being passed.
Including the "app_name" has said by most Django devs makes the code neat and easy to streamline to as to which url you want and most importantly helps to avoid duplicating urls..
An instance will be having this url path of "home" in your app accounts and also having that same path "home" in some other app, the use of specifying the app_name will help Django , include the urls to their respective apps so you know which requires which .
Your urls are correct to me and since it's a NoReverseMatch error, it will have to be your at your html or view or anywhere where you are calling a url.
Try including the app name in either your html and view on where you are calling the url..
Something like this
{% app_name:name_url %}
And same to the view app_name:name_url
Always make sure there is semi-colon between the app_name and the url ... As it will help you tell Django as to which url from which app!!

Django HoneyPot Change Password Issue

I would appreciate if you could give me any clue! As I don't have experience in this, probably I've misunderstood smth.
I'm using honeypot, more specifically honeypot.middleware.HoneypotMiddleware with HONEYPOT_FIELD_NAME in my API (settings.py).
As for the moment it's enough, I'm using the basic implementation for login, password change, reset from django.contrib.auth. In login I did a small customization so I added it in the url (authentication_form=CustomAuthenticationForm).
So I don't konw what I'm missing because the login page works (it is also a form), but the password change, reset ones are returning 400 Bad Request. Honey Pot Error (honey_pot_fieldname). Request aborted.
django: 2.1.2
django-honeypot: 0.7.0
[Updating with code]
settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
....
'honeypot',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'honeypot.middleware.HoneypotMiddleware',
]
HONEYPOT_FIELD_NAME = config('HONEYPOT_FIELD_NAME')
forms.py
from django.contrib.auth.forms import AuthenticationForm
class CustomAuthenticationForm(AuthenticationForm):
error_messages = {
'invalid_login': _(
"Please enter a correct %(username)s and password. Note that both "
"fields may be case-sensitive."
),
'inactive': _("This account is inactive."),
'suspended': _("Your account has been temporarily suspended. For more information,\
please contact us."),
}
def clean(self)
def confirm_login_allowed(self, user)
urls.py
from django.contrib.auth import views as auth_view
urlpatterns = [
....
# ---- BASIC USER AUTHENTICATION (DJANGO)
path('api/login/', auth_view.LoginView.as_view(authentication_form=CustomAuthenticationForm), name='login'),
path('api/logout/', auth_view.LogoutView.as_view(), name='logout'),
# ---- PASSWORD CHANGE, RESET
path('api/password_change/', auth_view.PasswordChangeView.as_view(), name='password_change'),
path('api/password_change_done/', auth_view.PasswordChangeDoneView.as_view(),
name='password_change_done'),
....
]
[Templates]
templates/resgistration/login.html
{% extends 'base.html' %}
{% load i18n widget_tweaks %}
{% block head %}
{% load bootstrap3 %}
{{ form.media }}
{% endblock %}
{% block content %}
<div class="col-sm-4 col-sm-offset-4" style="margin-top:20px;">
<h1 class="display-4 text-center" >User login</h1>
<legend></legend>
<form method="post">
{% csrf_token %}
{% bootstrap_form form layout='inline' %}
{% buttons %}
<button type="submit" class="btn btn-primary center-block" style="margin-top: 20px">Log in</button>
{% endbuttons %}
</form>
</div>
{% endblock %}
templates/resgistration/password_change_form.html
{% extends 'base.html' %}
{% load i18n widget_tweaks %}
{% block head %}
{% load bootstrap3 %}
{{ form.media }}
{% endblock %}
{% block content %}
<div class="col-sm-4 col-sm-offset-4" style="margin-top:20px;">
<h1 class="display-4 text-center" >{{ title }}</h1>
<legend></legend>
<form method="post">
{% csrf_token %}
{% bootstrap_form form layout='inline' %}
{% buttons %}
<button type="submit" class="btn btn-primary center-block" style="margin-top: 20px">Change password</button>
{% endbuttons %}
</form>
</div>
{% endblock %}
Thanks in advance!
[Updating with solution]
Finally I achieved it!
I only had to add at the top of the template change_password_form.html the line
{% load honeypot %}
and within the form including the tag:
{% render_honeypot_field "field_name" %}
For achieve this helped me this article
django-honeypot
Hope that will be helpful for others.

Django: login link to appear only when a user is logged in

I am making an app in django in which i have used the django built in authentication system. Both login and logout links are present in the navbar. I want to make the logout link appear only when the user has logged in and not at all times. How do i do that?
code snippet of project/urls.py:
urlpatterns = [
url(r'^login/$', views.login, {'template_name': 'login.html', 'authentication_form': LoginForm}, name='login'),
url(r'^logout/$', views.logout, {'next_page': '/home'}), ]
code snippet of login.html;
<div class="container">
<section id="content">
{% if form.errors %}
<p>Your username and password didn't match. Please try again.</p>
{% endif %}
{% if next %}
{% if user.is_authenticated %}
<p>Your account doesn't have access to this page. To proceed,
please login with an account that has access.</p>
{% else %}
<p>Please login to see this page.</p>
{% endif %}
{% endif %}
<form action="{% url 'login' %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<h1>Login Form</h1>
<div class="imgcontainer">
<img src="{% static 'student/patient.jpg' %}" alt="Avatar" class="avatar">
</div>
<div class="username">
{{ form.username.label_tag }}
{{ form.username }}
</div>
<div class="password">
{{ form.password.label_tag }}
{{ form.password }}
</div>
<div class="submitb">
<input type="submit" value="Log In" name="mybtn">
</div>
<div class="resetb">
<input type="submit" value="Reset">
Forgot password?
</div>
<input type="hidden" name="next" value="{{ next }}" />
</form>
code snippet of base.html(only the navbar is shown):
<ul>
<li><a class="active" href="/home">Home</a></li>
<li>About </li>
<li>Sign up</li>
<li>Doctor's login</li>
<li>Patient's login</li>
<li>FAQs</li>
<li>Contact us</li>
<li>Reviews</li>
<li>Logout</li>
</ul>
Thanks for your help in advance.
Put the logout list item in an if block that checks that the user is authenticated like so:
<ul>
...
{% if request.user.is_authenticated %}
<li>Logout</li>
{% endif %}
</ul>
Although using the variable request.user.is_authenticated is one way. To make it simpler create HTML pages such that login and logout button are disintegrated.
Pages which appear after login should only contain the logout option/button. This will smoothen your development process.

how to add change language dropdown to Django(1.10) admin?

I'm planning to add change language dropdown in my admin page.
according to this code and How to extend admin page.
I copy base_site.html and copy it to myapp/templates/admin, the i create a html file named change_language.html and write this code in it:
{% load i18n %}
/ {% trans 'Change language' %}
<form action="/i18n/setlang/" method="post" style="display: inline;">
<div style="display: inline;">
<select name="language" onchange="javascript:form.submit()">
{% for lang in LANGUAGES %}
<option value="{{ lang.0 }}"{% ifequal LANGUAGE_CODE lang.0 %} selected="selected"{% endifequal %}>{{ lang.1 }}</option>
{% endfor %}
</select>
</div>
</form>
I add {% extends 'admin/base_site.html' %} at the top of this file, noting happens.
I add {% extends 'admin/base.html' %} , again noting happens.
All hints and answers says that we should change something name <div id="user-tools"> at line 25 of base.html, But in Django 1.10 it goes to line 31 with a different staff. im kinnda lost because i read many different staff every where and non of them works for me. Dose any know where im doing wrong ?
here is my middlewares :
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
And template settings :
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
os.path.join(BASE_DIR,'templates'),
],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
In your templates/admin folder, make sure the file is named base_site.html (otherwise the default base_site will not be overwritten).
Make sure to copy the latest version of the file from the django-repo.
Most important: verify you extend the admins base.html (your base_site.html should start with {% extends "admin/base.html" %})
You may now add your form (for example to the footer):
{% extends "admin/base.html" %}
{% load i18n %}
{% block title %}{{ title }} | {{ site_title|default:_('Django site admin') }}{% endblock %}
{% block branding %}
<h1 id="site-name">{{ site_header|default:_('Django administration') }}</h1>
{% endblock %}
{% block nav-global %}{% endblock %}
{% block footer %}
<div id="footer">
<form action="/i18n/setlang/" method="post">
{% csrf_token %}
<select name="language">
{% for lang in LANGUAGES %}
<option value="{{ lang.0 }}"
{% ifequal lang.0 request.LANGUAGE_CODE %}
selected="yes"
{% endifequal %}
>{{ lang.1 }}</option>
{% endfor %}
</select>
<input type="submit" value="{% trans 'Change language' %}" />
</form>
</div>
{% endblock %}
Refer to the base.html to find a block that seems suitable for your form.
You will also need to add the i18n urls to your url-settings
url(r'^i18n/', include('django.conf.urls.i18n')),
And if you really want to submit your form using javascript you will also need to get and submit the csrf token for the form. The django docs cover this topic quite comprehensive.
In addition to Kim's answer, you should add i18n context processor in your django settings django.template.context_processors.i18n to be able to access LANGUAGES variable in the template.
I guess you are mixing both answers that you've found on the internet. One of them copies and changes a couple of files from the admin template, effectively overriding them in your program's references. The second one extends admin's templates. You should completely implement just one of them.

How does Django 1.4 CSRF work?

I have that urls.py:
from django.conf.urls import patterns, url
urlpatterns = patterns('',
url(
r'^login/$',
'django.contrib.auth.views.login',
{'template_name': 'loyalty/login.html'},
name='login'
),
url(
r'^logout/$',
'django.contrib.auth.views.logout',
{'next_page': '/', },
name='logout'),
)
and have template login.html*:
{% extends "loyalty/auth.html" %}
{% load i18n %}
{% block auth_form %}
{% if messages %}
<ul>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
<form action="" method="post" id="login-form">{% csrf_token %}
{% if form.username.errors %}{{ form.username.errors }}{% endif %}
{{ form.username }}
{% if form.password.errors %}{{ form.password.errors }}{% endif %}
{{ form.password }}
<input type="hidden" name="this_is_the_login_form" value="1" />
<input type="hidden" name="next" value="{{ next }}" />
<button type="submit" name="submit">{% trans 'Log in' %}</button>
</form>
{% endblock %}
and i have in settings.py:
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
# Uncomment the next line for simple clickjacking protection:
# 'django.middleware.clickjacking.XFrameOptionsMiddleware',
)
But this gives me this error:
Forbidden (403)
CSRF verification failed. Request aborted.
Help
Reason given for failure:
CSRF cookie not set.
In administrative interface, I have same problem.
What can I do to solve this problem?
You probably aren't passing the Request context from your view.
Example:
def show_form(request):
form = MyForm()
if request.method == 'POST':
form = MyForm(request.POST)
if form.is_valid():
return render_to_response("template_to_display.html", {'form':form}, context_instance = template.RequestContext(request))
Update: I would suggest rearranging your middleware. Try removing the localemiddleware or placing it at last. The django default is this
I solved this problem. Problem in browser. Tried an other browser and all works fine. Didn't understand why I had this problem, because I tried clean cache and cookies.

Categories