I am using Django to build a web app. I am using Vue JS for the frontend. My problem is when ever I use csrf_protect its showing 403 error
My views:
#csrf_protect
def SignUpView(request):
if request.method == "POST":
form = SignUpForm(request.POST)
if form.is_valid():
form.save()
username, password = form.cleaned_data.get('username'), form.cleaned_data.get('password1')
new_user = authenticate(username = username, password = password)
login(request, new_user)
return redirect('/')
else:
form = SignUpForm()
return render(request, 'Accounts/SignUp.html', {'form':form})
#csrf_protect
def validateUsername(request):
username = request.GET.get('username', None)
usernameRegEx = r'^[a-zA-Z0-9#+-_.#]*$'
usernameRegExResult = {
'valid' : bool(re.search(usernameRegEx, username, re.M|re.I)),
'is_taken' : User.objects.filter(username=username).exists()
}
return JsonResponse(usernameRegExResult)
I read the Django docs which says I can use csrf_protect decorator above my view but in my case its not working. Somebody please help.
CSRF is a protection that prevents cross site request forgery. It works by generating an unique token that identify the form. So if you send data to your server without the token it gave you (through cookies for instance) it will not accept it.
If you have the CSRF middleware turned on you should not need CSRF protect decorator!
I am trying to create a user login. I am registering the user through django's admin page. Username and passwords are entered correctly.
Also I have tried adding authentication backends to settings.py
I have tried multiple ways but couldn't get it to work.
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
)
My code looks like below :
models.py :
class Account(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
views.py:
def login(request):
if request.method == 'POST':
username = request.POST.get('user')
password = request.POST.get('pass')
user = authenticate(username=username,
password=password) ----------> None
if user:
if user.is_active():
login(request, user)
return HttpResponseRedirect(reverse('index'))
else:
return HttpResponse('Account not active')
else:
print('Someone tried to login and failed ')
print('Username {} and passowrd {}'.format(username, password))
return HttpResponse('Invalid login details supplied!!')
else:
return render(request,'account/login.html', {})
The username and password from the below ones are coming as empty so it is giving none
username = request.POST.get('user')
password = request.POST.get('pass')
Try to inspect the element from HTML to find the name of the fields from which the data is coming from the template or print the values of username and password variables to cross verify.
use request.POST.get('username'), request.POST.get('password1') but as told it totally depends on the html template. It is safer to clean the data and then go for authentication.
Django has inbuilt Class Based Views for the same purpose. Give them a try
I am using django-rest-jwt for authentication in my app.
By default it user username field to autenticate a user but I want let the users login using email or username.
Is there any mean supported by django-rest-jwt to accomplish this.
I know the last option would be write my own login method.
No need to write a custom authentication backend or custom login method.
A Custom Serializer inheriting JSONWebTokenSerializer, renaming the 'username_field' and overriding def validate() method.
This works perfectly for 'username_or_email' and 'password' fields where the user can enter its username or email and get the JSONWebToken for correct credentials.
from rest_framework_jwt.serializers import JSONWebTokenSerializer
from django.contrib.auth import authenticate, get_user_model
from django.utils.translation import ugettext as _
from rest_framework import serializers
from rest_framework_jwt.settings import api_settings
User = get_user_model()
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
jwt_decode_handler = api_settings.JWT_DECODE_HANDLER
jwt_get_username_from_payload = api_settings.JWT_PAYLOAD_GET_USERNAME_HANDLER
class CustomJWTSerializer(JSONWebTokenSerializer):
username_field = 'username_or_email'
def validate(self, attrs):
password = attrs.get("password")
user_obj = User.objects.filter(email=attrs.get("username_or_email")).first() or User.objects.filter(username=attrs.get("username_or_email")).first()
if user_obj is not None:
credentials = {
'username':user_obj.username,
'password': password
}
if all(credentials.values()):
user = authenticate(**credentials)
if user:
if not user.is_active:
msg = _('User account is disabled.')
raise serializers.ValidationError(msg)
payload = jwt_payload_handler(user)
return {
'token': jwt_encode_handler(payload),
'user': user
}
else:
msg = _('Unable to log in with provided credentials.')
raise serializers.ValidationError(msg)
else:
msg = _('Must include "{username_field}" and "password".')
msg = msg.format(username_field=self.username_field)
raise serializers.ValidationError(msg)
else:
msg = _('Account with this email/username does not exists')
raise serializers.ValidationError(msg)
In urls.py:
url(r'{Your url name}$', ObtainJSONWebToken.as_view(serializer_class=CustomJWTSerializer)),
Building on top of Shikhar's answer and for anyone coming here looking for a solution for rest_framework_simplejwt (since django-rest-framework-jwt seems to be dead, it's last commit was 2 years ago) like me, here's a general solution that tries to alter as little as possible the original validation from TokenObtainPairSerializer:
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
class CustomJWTSerializer(TokenObtainPairSerializer):
def validate(self, attrs):
credentials = {
'username': '',
'password': attrs.get("password")
}
# This is answering the original question, but do whatever you need here.
# For example in my case I had to check a different model that stores more user info
# But in the end, you should obtain the username to continue.
user_obj = User.objects.filter(email=attrs.get("username")).first() or User.objects.filter(username=attrs.get("username")).first()
if user_obj:
credentials['username'] = user_obj.username
return super().validate(credentials)
And in urls.py:
url(r'^token/$', TokenObtainPairView.as_view(serializer_class=CustomJWTSerializer)),
Found out a workaround.
#permission_classes((permissions.AllowAny,))
def signin_jwt_wrapped(request, *args, **kwargs):
request_data = request.data
host = request.get_host()
username_or_email = request_data['username']
if isEmail(username_or_email):
# get the username for this email by model lookup
username = Profile.get_username_from_email(username_or_email)
if username is None:
response_text = {"non_field_errors":["Unable to login with provided credentials."]}
return JSONResponse(response_text, status=status.HTTP_400_BAD_REQUEST)
else:
username = username_or_email
data = {'username': username, 'password':request_data['password']}
headers = {'content-type': 'application/json'}
url = 'http://' + host + '/user/signin_jwt/'
response = requests.post(url,data=dumps(data), headers=headers)
return JSONResponse(loads(response.text), status=response.status_code)
I check that whether the text that I received is a username or an email.
If email then I lookup the username for that and then just pass that to /signin_jwt/
authentication.py
from django.contrib.auth.models import User
class CustomAuthBackend(object):
"""
This class does the athentication-
using the user's email address.
"""
def authenticate(self, request, username=None, password=None):
try:
user = User.objects.get(email=username)
if user.check_password(password):
return user
return None
except User.DoesNotExist:
return None
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
settings.py
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.ModelBackend',
'app_name.authentication.CustomAuthBackend',
]
How it works:
If user try to authenticate using their username django will look at the ModelBackend class. However, if the user adds its email instead, django will try ModelBackend but will not find the logic needed, then will try the CustomAuthBackend class making it work the authentication.
Alternatively, this new DRF Auth project dj-rest-auth seems to provide support for log in by username or email through djangorestframework-simplejwt.
dj-rest-auth works better for authentication and authorization. By default dj-rest-auth provides - username, email and password fields for login. User can provide email and password or username and password. Token will be generated, if the provided values are valid.
If you need to edit these login form, extend LoginSerializer and modify fields. Later make sure to add new custom serializer to settings.py.
REST_AUTH_SERIALIZERS = {
'LOGIN_SERIALIZER': 'yourapp.customlogin_serializers.CustomLoginSerializer'
}
Configuring dj-rest-auth is bit tricky, since it has an open issue related to the refresh token pending. There is workaround suggested for that issue, so you can follow below links and have it configured.
https://medium.com/geekculture/jwt-authentication-in-django-part-1-implementing-the-backend-b7c58ab9431b
https://github.com/iMerica/dj-rest-auth/issues/97
If you use the rest_framework_simplejwt this is a simple mode. views.py
from rest_framework_simplejwt.tokens import RefreshToken
from django.http import JsonResponse
from rest_framework import generics
class EmailAuthToken(generics.GenericAPIView):
def post(self, request):
user_data = request.data
try:
user = authenticate(request, username=user_data['username_or_email'], password=user_data['password'])
if user is not None:
login(request, user)
refresh = RefreshToken.for_user(user)
return JsonResponse({
'refresh': str(refresh),
'access': str(refresh.access_token),
}, safe=False, status=status.HTTP_200_OK)
else:
return JsonResponse({
"detail": "No active account found with the given credentials"
}, safe=False, status=status.HTTP_200_OK)
except:
return Response({'error': 'The format of the information is not valid'}, status=status.HTTP_401_UNAUTHORIZED)
I want to use Tastypie's ApiKeyAuthentication to authenticate a request and then establish a session for the user within a Django view. I have username and api_key for the user. I do not have the user's password. This is the code I currently have:
class ApiKeyPlusWebAuthentication(ApiKeyAuthentication):
def is_authenticated(self, request, **kwargs):
isAuthenticated = super(ApiKeyPlusWebAuthentication, self).is_authenticated(request, **kwargs)
if isAuthenticated:
print request.user.email
return isAuthenticated
#login for access from UIWebView
def login_usingApiKeyAuthentication(request):
auth = ApiKeyPlusWebAuthentication(request)
if auth.is_authenticated(request):
print 'authenticated'
login(request, request.user)
return redirect(reverse(view_name))
else:
print 'NOT authenticated'
messages.error(request, MESSAGE_INVALID_LOGIN)
fail_redirect = redirect(reverse('login'))
return fail_redirect
I am getting an error 'User' object has no attribute 'backend'. This is because I haven't called authenticate(user, password). I am using the Django default authentication backend.
In this scenario, I only have APIKey associated with the user and don't have the raw password for authentication.
One way to handle this may be to create custom authentication backend that bypasses password requirement. However, registering a "password-less" authentication backend in settings.py seems like a hack prone to security breakdown.
So, how can I use ApiKeyAuthentication and then authenticate & login the user in Django establishing a session?
I found a solution to set the backend in another post. You can set the custom backend directly on the user object.
from django.contrib.auth.backends import ModelBackend
from django.contrib.auth.models import User
class PasswordlessAuthBackend(ModelBackend):
"""Log in to Django without providing a password.
"""
def authenticate(self, username=None):
try:
return User.objects.get(username=username)
except User.DoesNotExist:
return None
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
#csrf_exempt
def login_uiwebview(request):
auth = ApiKeyPlusWebAuthentication(request)
if auth.is_authenticated(request):
view_name = request.POST.get('view_name')
request.user.backend = 'app.views.PasswordlessAuthBackend'
login(request, request.user)
return redirect(view_name)
else:
print 'NOT authenticated'
messages.error(request, MESSAGE_INVALID_LOGIN)
fail_redirect = redirect(reverse('login'))
return fail_redirect
I'm trying to use Django's built in authentication modules. For the site I'm working on I want to use email addresses as login names and not just the normal alphanumeric fields they're usually set to. In order to do this I changed all the String fields to Email fields and changed their max length from 30 to 320. My registration code appears to be working fine but not my login code. Here is what I'm using right now:
def login(request):
if request.method == 'POST':
form = AuthenticationForm(request.POST)
if form.is_valid():
return HttpResponse("valid")
username = request.POST['username']
password = request.POST['password']
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
login(request, user)
return HttpResponseRedirect("/")
# Redirect to a success page.
else:
return HttpResponse("Disabled Account")
# Return a 'disabled account' error message
else:
return HttpResponse("Invalid Login")
# Return an 'invalid login' error message.
else:
return HttpResponse("%s" % repr(form.errors))
else:
form = AuthenticationForm()
return render_to_response("login.html", {'form': form, }, context_instance=RequestContext(request))
No matter what I submit, form.is_valid() is returning FALSE but form.errors is empty. Any ideas what might be wrong? I think I changed everything over to Email properties so I don't think that's it. Also, in case it changes anything I'm trying to do this on google app engine using djangoappengine.
Sorry, but you cannot use Django's authentication module on top of google app engine. Django uses its own special database backend which is similar to google-app-engine's but is not drop-in compatible.
If you want to do authentication on GAE, you should do it the google-app-engine way:
http://code.google.com/appengine/docs/python/users/