I have authentication app with email verification in it. I send an email like this:
If registration form is valid then we save the form(create user) and set his token to uuid.uuid4.
class customer_register(CreateView):
model = User
form_class = CustomerSignUpForm
template_name = 'authentication/customer_register.html'
def form_valid(self, form):
user = form.save()
user.token = str(uuid.uuid4)
subject = 'Verify your account | Zane'
message = f"http://127.0.0.1:8000/verify/{user.token}/"
send_mail(
subject,
message,
'from#example.com',
['to#example.com'],
fail_silently=False,
)
In my mailtrap.io email arrives but it has some weird body:
http://127.0.0.1:8000/verify/<function uuid4 at 0x103f32040>/
Please use str(uuid.uuid4()) instead of str(uuid.uuid4)
I'm verifying the user OTP to change password and after change password I'm unable to create access and refresh token using JWT ,
Normally when user get log in I use following method MyTokenObtainPairView which return both access and refresh token with all other stuff to UserSerializerWithToken.
class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
def validate(self, attrs):
data = super().validate(attrs)
serializer = UserSerializerWithToken(self.user).data
for k, v in serializer.items():
data[k] = v
return data
class MyTokenObtainPairView(TokenObtainPairView):
serializer_class = MyTokenObtainPairSerializer
I coppied similar appraoch to return UserSerializerWithToken after set_password and user.save()
UserSerializerWithToken is
class UserSerializerWithToken(UserSerializer):
token = serializers.SerializerMethodField(read_only=True)
class Meta:
model = CustomUser
fields = ['id',
'isAdmin',
'token']
def get_token(self, obj):
token = RefreshToken.for_user(obj)
return str(token.access_token)
and the problematic function is
#api_view(['PUT'])
def reset_password(request):
data = request.data
email = data['email']
otp_to_verify = data['otp']
new_password = data['password']
user = CustomUser.objects.get(email=email)
serializer = UserSerializerWithToken(user, many=False)
if CustomUser.objects.filter(email=email).exists():
if otp_to_verify == user.otp:
if new_password != '':
user.set_password(new_password)
user.save() # here password gets changed
return Response(serializer.data) #
else:
message = {
'detail': 'Password cant be empty'}
return Response(message, status=status.HTTP_400_BAD_REQUEST)
else:
message = {
'detail': 'Something went wrong'}
return Response(message, status=status.HTTP_400_BAD_REQUEST)
I receive the toke but not getting access and refresh toke to use it to login next time. I'm assuming user.save() dosnt create refresh and access token here. Can anybody identify why this is happening and how to fix that
user.save() does not create the tokens
token = RefreshToken.for_user(obj)
return str(token.access_token)
These lines create the token.
In my opinion, you dont need the serializer here.
#api_view(['PUT'])
def reset_password(request):
data = request.data
email = data['email']
otp_to_verify = data['otp']
new_password = data['password']
user = CustomUser.objects.get(email=email)
if CustomUser.objects.filter(email=email).exists():
otp_to_verify == user.otp
if new_password != '':
user.set_password(new_password)
user.save() # here password gets changed
token = RefreshToken.for_user(user)
response = { "refresh_token": str(token),
"access_token": str(token.access_token)
}
return Response(response)
else:
message = {
'detail': 'Password cant be empty'}
return Response(message, status=status.HTTP_400_BAD_REQUEST)
else:
message = {
'detail': 'Something went wrong'}
return Response(message, status=status.HTTP_400_BAD_REQUEST)
When you save user, you can make a post request:
access_url = config('BASE_API_URL') + 'token/'
access_response = requests.post(access_url, data=data)
access_token = access_response.json().get('access')
refresh_token = access_response.json().get('refresh')
Then return,
return Response(
{"access_token": access_token,
"refresh_token" : refresh_token,
"additional_data": messege},
status=status.HTTP_200_OK
)
I'm using all-auth and dj-rest-auth to implement registration and login via email. Everything work fine but when making test, inactive users return invalid credentials message instead inactive account message. In the LoginSerializer, it seems that django.contrib.auth authenticate method doesn't return the user, but None. Here is the code:
settings.py
AUTHENTICATION_BACKENDS = [
"django.contrib.auth.backends.AllowAllUsersModelBackend",
"allauth.account.auth_backends.AuthenticationBackend"
]
REST_AUTH_REGISTER_SERIALIZERS = {
'REGISTER_SERIALIZER': 'user.serializers.RegisterSerializer',
}
REST_AUTH_SERIALIZERS = {
'LOGIN_SERIALIZER': 'user.serializers.LoginSerializer',
'USER_DETAILS_SERIALIZER': 'user.serializers.UserDetailSerializer',
}
serializers.py
class LoginSerializer(serializers.Serializer):
email = serializers.EmailField(required=True, allow_blank=False)
password = serializers.CharField(style={'input_type': 'password'})
def authenticate(self, **kwargs):
return authenticate(self.context['request'], **kwargs)
def _validate_email(self, email, password):
user = None
if email and password:
user = self.authenticate(email=email, password=password)
else:
msg = _('Must include "email" and "password".')
raise exceptions.ValidationError(msg)
return user
def validate(self, attrs):
email = attrs.get('email')
password = attrs.get('password')
user = None
if 'allauth' in settings.INSTALLED_APPS:
from allauth.account import app_settings
# Authentication through email
if app_settings.AUTHENTICATION_METHOD == app_settings.AuthenticationMethod.EMAIL:
user = self._validate_email(email, password)
# Did we get back an inactive user?
if user:
if not user.is_active:
msg = _('User account is disabled.')
raise exceptions.ValidationError(msg)
else:
msg = _('Unable to log in with provided credentials.')
raise exceptions.ValidationError(msg)
# If required, is the email verified?
if 'dj_rest_auth.registration' in settings.INSTALLED_APPS:
from allauth.account import app_settings
if app_settings.EMAIL_VERIFICATION == app_settings.EmailVerificationMethod.MANDATORY:
try:
email_address = user.emailaddress_set.get(email=user.email)
except:
raise serializers.ValidationError(_('E-mail is not registered.'))
else:
if not email_address.verified:
raise serializers.ValidationError(_('E-mail is not verified.'))
attrs['user'] = user
return attrs
tests.py
########################################################################
# LOG IN WITH INACTIVE USER
login_data = {
'email': 'inactive#inactive.com',
'password': '9I8u7Y6t5R4e'
}
response = self.client.post('http://localhost:8000/api/auth/login/', login_data)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
expected_error = {
'non_field_errors': 'User account is disabled.'
}
response_error = {
'non_field_errors': response.data['non_field_errors'][0]
}
self.assertEqual(response_error, expected_error)
Is there something that I missing?
Thanks in advance.
In case anyone is interested, I found the problem: the allauth authentication backend override the django model backend. In order to resolve this, I create a class that inherit from allauth backend and add the function that allow all users to log in:
backend.py
from allauth.account.auth_backends import AuthenticationBackend
class AllowAllUsersModelBackend(AuthenticationBackend):
def user_can_authenticate(self, user):
return True
Then add it to settings:
settings.py
AUTHENTICATION_BACKENDS = [
"user.backends.AllowAllUsersModelBackend",
]
I am working on a authentication application where user's can login via
(email or mobile) and (password or otp)
Framework/ Library Used
Django Rest Framework and djangorestframework-simplejwt
I am trying to add multiple claims in the jwt token getting generated.
Below is my LoginView and LoginSerializer.
View
class Login(TokenObtainPairView):
serializer_class = LoginSerializer
Serializer
class LoginSerializer(TokenObtainPairSerializer):
mobile = serializers.CharField(allow_blank=True)
email = serializers.EmailField(allow_blank=True)
password = serializers.CharField(allow_blank=True)
otp = serializers.CharField(allow_blank=True)
#classmethod
def get_token(cls, user):
token = super().get_token(user)
token['name'] = user.first_name
return token
def validate(self, attrs):
mobile = attrs.get("mobile", None)
email = attrs.get("email", None)
password = attrs.get("password", None)
otp = attrs.get("otp", None)
user = authenticate(mobile=mobile, email=email, password=password, otp=otp)
if user is None and self.password:
raise serializers.ValidationError(
detail="Incorrect Username or Password.", code=HTTP_401_UNAUTHORIZED
)
if user.is_active:
refresh = self.get_token(user)
data = dict()
data['refresh'] = str(refresh)
data['access'] = str(refresh.access_token)
return data
if user.is_locked:
raise serializers.ValidationError(
detail="Account Locked. Contact Support.", code=HTTP_423_LOCKED
)
raise serializers.ValidationError(
detail="User Account is Deactivated.",
code=HTTP_401_UNAUTHORIZED
)
But i am getting email can't be blank error when sending a valid phone number and password in request. This is because of TokenObtainPairSerializer which checks for User.USERNAME_FIELD (which in my case is email.)
How can i handle this situation or get it working ?
Okey so I wish to create really simple user managment for my site using django(1.6), tastyie and angularjs. I want to be able to signin/up/out. I have basically implemented the solution from here: How can I login to django using tastypie
my code looks like this:
resources:
from tastypie.resources import ModelResource, ALL, ALL_WITH_RELATIONS
from photod.models import Project, ProjectImage
from tastypie.authorization import DjangoAuthorization,Authorization
from tastypie.authentication import BasicAuthentication,Authentication
from django.contrib.auth.models import User
from django.contrib.auth import authenticate, login, logout
from tastypie import fields
from tastypie.serializers import Serializer
from tastypie.exceptions import BadRequest
from django.db import IntegrityError
from tastypie.http import HttpUnauthorized, HttpForbidden
from django.conf.urls import url
from tastypie.utils import trailing_slash
class UserResource(ModelResource):
class Meta:
queryset = User.objects.all()
resource_name = 'user'
excludes = ['email', 'password', 'is_active', 'is_staff', 'is_superuser']
serializer = Serializer(formats=['json', 'jsonp'])
always_return_data = True
filtering = {
'username': 'exact',
'id': ALL_WITH_RELATIONS,
}
def prepend_urls(self):
return [
url(r"^(?P<resource_name>%s)/login%s$" %
(self._meta.resource_name, trailing_slash()),
self.wrap_view('login'), name="api_login"),
url(r'^(?P<resource_name>%s)/logout%s$' %
(self._meta.resource_name, trailing_slash()),
self.wrap_view('logout'), name='api_logout'),
]
def login(self, request, **kwargs):
self.method_check(request, allowed=['post'])
data = self.deserialize(request, request.body, format=request.META.get('CONTENT_TYPE', 'application/json'))
username = data.get('username', '')
password = data.get('password', '')
user = authenticate(username=username, password=password)
if user:
if user.is_active:
login(request, user)
return self.create_response(request, {
'success': True
})
else:
return self.create_response(request, {
'success': False,
'reason': 'disabled',
}, HttpForbidden )
else:
return self.create_response(request, {
'success': False,
'reason': 'incorrect user information',
}, HttpUnauthorized )
def logout(self, request, **kwargs):
self.method_check(request, allowed=['get'])
if request.user and request.user.is_authenticated():
logout(request)
return self.create_response(request, { 'success': True })
else:
return self.create_response(request, { 'success': False }, HttpUnauthorized)
lass CreateUserResource(ModelResource):
class Meta:
allowed_methods = ['post']
object_class = User
resource_name = 'register'
queryset = User.objects.all()
authentication = Authentication()
authorization = Authorization()
include_resource_uri = False
fields = ['username', 'id']
always_return_data = True
def obj_create(self, bundle, request=None, **kwargs):
username, password = bundle.data['username'], bundle.data['password']
try:
bundle.obj = User.objects.create_user(username, '', password)
except IntegrityError:
raise BadRequest('That username already exists')
return bundle
front end:
photodice.controller('userController', function($scope, $resource, userFactory) {
$scope.userName = '';
$scope.userPw = '';
$scope.Signup = function() {
var userC = $resource("http://xx.xxx.xxx.xx:xxxx/api/v1/register/");
var newUser = new userC();
newUser.username = $scope.userName;
newUser.password = $scope.userPw;
newUser.$save(function (user, headers) {// Success
userFactory.setUser(user.username, user.id);
console.log("$save (signUp) success " + JSON.stringify(user));
$scope.userName = '';//cleanup
$scope.userPw = '';//cleanup
}, function (error) {// failure - TODO: add message
console.log("$save (signUp) failed " + JSON.stringify(error.data.error_message))
});
}
$scope.Signin = function() {
var userResource = $resource('http://xx.xxx.xxx.xx:xxxx/api/v1/user/login/');
user = new userResource();
user.username = $scope.userName;
user.password = $scope.userPw;
user.$save(function () {// Success
userFactory.setUser($scope.userName);
console.log("$save (signIn) success " + JSON.stringify(user));
$scope.userName = '';//cleanup
$scope.userPw = '';//cleanup
}, function (error) {// failure - TODO: add message
console.log("$save (signIn) failed " + JSON.stringify(error.data.error_message));
});
}
$scope.Signout = function() {
console.log("called signout");
var userResource = $resource('http://xx.xxx.xxx.xx:xxxx/api/v1/user/logout/:user', { user:'#user' });
var user = userResource({user:userFactory.getUser()}, function() {
user.$save(function (user, headers) {// Success
userFactory.setUser('');
console.log("$save (signIn) success " + JSON.stringify(user));
}, function (error) {// failure - TODO: add message
console.log("$save (signIn) failed " + JSON.stringify(error.data.error_message));
});
});
}
});
But I have some problems and questions:
Firstly I can sign in with the superuser account, but i only get {"success":true,"$resolved":true} as respons... should i not get some sort of token or id or more data?
Secoundly i can signup new users, but they can NOT sign in as i get: 401 (UNAUTHORIZED)
Edit: upon further investigation i notised that although I can sign up new users, thay do not get any password set... why is this?
1
But I have some problems and questions: Firstly I can sign in with the
superuser account, but i only get {"success":true,"$resolved":true} as
respons... should i not get some sort of token or id or more data?
What you do in login is assigning request with user. You authenticated user here: user = authenticate(username=username, password=password) and assigned that user to request here: login(request, user). So Django will now recognize request.user as that user during your session.
You haven't defined authentication method in your resource therefore is default. It gives access to anonymous users also so don't have to be even authenticated to have access. Once your decide which authentication you want to use then you will think about tokens and stuff.
See this: Authentication in Tastypie
2
Secoundly i can signup new users, but they can NOT sign in as i get:
401 (UNAUTHORIZED)
Your are seeing this most likely because your password or username is incorrect. user = authenticate(username=username, password=password) gives you user is None and your eles block is executed. You can make sure with printing logs in that step.
3
Edit: upon further investigation i notised that although I can sign up
new users, thay do not get any password set... why is this?
I tested the same code and works perfectly. Make sure you don't have typo on frontend side. And print logs with values in obj_create to make sure they aren't empty.
4
To allow session authentication is quite difficult and it is capable for another question. This make it possible to get request.user. (Very insecure but simple)
class PasswordAuthentication(Authentication):
def is_authenticated(self, request, **kwargs):
"""
Allow get not authenticated users but try assign user to request
if possible.
"""
try:
username, password = request.GET.get('username'), request.GET.get('password')
except ValueError:
return True
if not username or not password:
return True
try:
user = User.objects.get(username=username, password=password)
except (User.DoesNotExist, User.MultipleObjectsReturned):
return True
if not self.check_active(user):
return True
request.user = user
return True
class UserResource(ModelResource):
class Meta:
queryset = User.objects.all()
resource_name = 'user'
excludes = ['email', 'password', 'is_active', 'is_staff', 'is_superuser']
serializer = Serializer(formats=['json', 'jsonp'])
authentication = PasswordAuthentication()
always_return_data = True
filtering = {
'username': 'exact',
'id': ALL_WITH_RELATIONS,
}
[...]
def logout(self, request, **kwargs):
self.method_check(request, allowed=['get'])
if request.user and request.user.is_authenticated():
logout(request)
return self.create_response(request, { 'success': True })
else:
return self.create_response(request, { 'success': False }, HttpUnauthorized)
call backend with http://xx.xxx.xxx.xx:xxxx/api/v1/user/logout/4/?username=asdf&password=1234