my custom signup api
from rest_framework import viewsets
from rest_framework import serializers
class SignupSerializer(serializers.Serializer):
email = serializers.EmailField(required=True)
password = serializers.CharField(required=True, write_only=True)
def validate_email(self, val):
try:
User.objects.get(username=val)
raise serializers.ValidationError("Email-ID already Exist")
except User.DoesNotExist:
return val
class SignupView(viewsets.ModelViewSet):
serializer_class = SignupSerializer
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
data = serializer.data
email = data.get('email')
password = data.get('password')
u = User(email=email, username=email)
u.set_password(password)
u.save()
data = {
'message': 'Successfully Created!',
'data': {'id': u.id, 'email': email},
}
return Response(data)
Toekn get api
from rest_framework.authtoken import views
urlpatterns = [
url(r'^api-token-auth/', views.obtain_auth_token),
]
when I hit signup api, it returns the success response, I mean
{
'message': 'Successfully Created!',
'data': {'id': 1, 'email': 'xyz#gmail.com'} }
but when I try to generate/get token for this user using above token api, it says ..invalid credentials?
{
"non_field_errors": [
"Unable to log in with provided credentials."
]
}
however if I create a user using management command createsuperuser and use same api to get token it works?
is there someting wrong in signup api?
You should remove write_only=True from your serializer. that would fix the error.
The Error message actually tells you that your request is not able to reach the view, this happens because one of the middlewares raises an error.
The easiest (but pbly not very secure) solution would be to return the API token on successful signup, or to add another View with a login (which returns an token).
For a definite answer you would need to provide more information like the configuration for your Authentication Backends.
You can find here more Informations about Authentication in DRF
http://www.django-rest-framework.org/api-guide/authentication/#how-authentication-is-determined
Related
Hy there,
I work on project where I used django-rest-knox for token authentication. I have doubt that
1.How token be used that has return while registering and login.
(
when i pass token in postman as like,
in header section
Authentication Token abcjdkkfjjrhehrjlajn#kfjdk
) this doesnot work
2.when i call logout and logoutall endpoint it say,
{
"detail": "Authentication credentials were not provided."
}
even though i pass all correct credentials.
Here is the code that i follow,
in setting.py
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": (
'knox.auth.TokenAuthentication',
"rest_framework.authentication.BasicAuthentication",
"rest_framework.authentication.SessionAuthentication",)}
REST_AUTH_TOKEN_MODEL = 'knox.models.AuthToken'
REST_AUTH_TOKEN_CREATOR = 'users.authentication.create_knox_token'
REST_AUTH_SERIALIZERS = {
'USER_DETAILS_SERIALIZER': 'users.serializers.CustomUserSerializer',
'TOKEN_SERIALIZER': 'users.serializers.KnoxSerializer'
}
in urls.py
path('auth/register/',KnoxRegisterView.as_view(),name='register'),
path('auth/login/',KnoxLoginView.as_view(),name='login'),
path('api/auth/logout/',knox_view.LogoutView.as_view(),name='knox_login'),
path('api/auth/logoutall/',knox_view.LogoutAllView.as_view(),name='knox_alllogin'),
in authentication.py
from knox.models import AuthToken
def create_knox_token(token_model, user, serializer):
token = AuthToken.objects.create(user=user)
return token
in serializers.py
class KnoxSerializer(serializers.Serializer):
"""
Serializer for Knox authentication.
"""
token=serializers.CharField()
user = CustomUserDetailsSettingsSerializer()
in views.py
class KnoxRegisterView(RegisterView):
def get_response_data(self, user):
return KnoxSerializer({'user': user, 'token': self.token}).data
def perform_create(self, serializer):
user = serializer.save(self.request)
self.token = create_knox_token(None, user, None)
complete_signup(self.request._request, user, allauth_settings.EMAIL_VERIFICATION, None)
return user
class KnoxLoginView(LoginView):
def get_response(self):
serializer_class = self.get_response_serializer()
data = {
'user': self.user,
'token': self.token
}
serializer = serializer_class(instance=data, context={'request': self.request})
return Response(serializer.data, status=200)
I'm not sure but I think your problem is that you need to override the login view so it doesn't request authentication. Usually the rest framework is setup like this:
# setting.py
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
}
So, your framework thinks it need authentication to ALL views, including the login view (which is silly of course).
The solution is to rewrite the login view as the documentation note:
If it is your only default authentication class, remember to overwrite knox's LoginView, otherwise it'll not work, since the login view will require a authentication token to generate a new token, rendering it unusable.
Try to add to your login view:
class KnoxLoginView(LoginView):
...
permission_classes = (permissions.AllowAny,)
...
I'm using Django REST along with Djoser as my authentication service. However, when making a post request to the djoser auth endpoints, e.g. .../auth/token/login, the only response I get back is auth_token: xxxxx
E.g, if I make a successful post request such as:
{
"username": "joe#gmail.com",
"password": "test12345"
}
My response will be
"auth_token": "XXXXXXXX"
But I'd like to retrieve more than just a token, e.g.
"auth_token": "XXXXXXXX",
"user_id": 8
Any idea on how to make this happen?
Create your own custom token obtain view as below,
from rest_framework.authtoken.views import ObtainAuthToken
from rest_framework.authtoken.models import Token
class TokenObtainView(ObtainAuthToken):
def post(self, request, *args, **kwargs):
serializer = self.serializer_class(data=request.data,
context={'request': request})
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
token, created = Token.objects.get_or_create(user=user)
custom_response = {
'token': token.key,
'user_id': user.id
}
return Response(custom_response)
and then wire-up your TokenObtainView class in your urls.py
urlpatterns = [
# other patterns
path('path/to/your/custom/token/view/', views.TokenObtainView.as_view(), name='new-token,obtain-view')
]
Now onwards, you should use the endpoint, path/to/your/custom/token/view/ to get your customized response.
I am using django Django=2.1.7 and rest framework djangorestframework=3.9.2 This is my url for login
path('rest-auth/', include('rest_auth.urls')),
After authentication I got token but I need user id too. I tried to override the post method of rest_framework.authtoken.views.py file with the following code
def post(self, request, *args, **kwargs):
serializer = self.serializer_class(data=request.data,
context={'request': request})
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
token, created = Token.objects.get_or_create(user=user)
context = {
'token': token.key,
'user': user.id
}
return Response({'context': context})
Please help me figure out how to get user id with the token. This is my college project.
Note: I find many answers on stack overflow but none is helpful.
Use this Django RestFramework token authentication in order to use authentication. Here you can see how to authenticate, however if you want to use token authentication by default for all views you should add it in settings.py file as :
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
...
'rest_framework.authentication.TokenAuthentication',
...
),
}
or you should add it manually to views which requires token authentication. And in this views you can get authenticated user as request.user or self.request.user.
from rest_framework.authentication import TokenAuthentication
class ViewSetName(ViewSet):
authentication_classes = [TokenAuthentication]
I'm using Django JWT authentication with the Django Rest Framework.
How can I get user info of the logged in user after I retrieve the token?
just check your app settings file, whether you have specified the jwt authentication backend or not.
if it mentioned there and if you are using User model ( in otherwords django.contrib.auth.models.User) request.user will work
If you are using your own custom User model
from django.conf import settings
from rest_framework import authentication
from rest_framework import exceptions
from rest_framework.authentication import get_authorization_header
import CustomUser # just import your model here
import jwt
class JWTAuthentication(authentication.BaseAuthentication):
def authenticate(self, request): # it will return user object
try:
token = get_authorization_header(request).decode('utf-8')
if token is None or token == "null" or token.strip() == "":
raise exceptions.AuthenticationFailed('Authorization Header or Token is missing on Request Headers')
print(token)
decoded = jwt.decode(token, settings.SECRET_KEY)
username = decoded['username']
user_obj = CustomUser.objects.get(username=username)
except jwt.ExpiredSignature :
raise exceptions.AuthenticationFailed('Token Expired, Please Login')
except jwt.DecodeError :
raise exceptions.AuthenticationFailed('Token Modified by thirdparty')
except jwt.InvalidTokenError:
raise exceptions.AuthenticationFailed('Invalid Token')
except Exception as e:
raise exceptions.AuthenticationFailed(e)
return (user_obj, None)
def get_user(self, userid):
try:
return CustomUser.objects.get(pk=userid)
except Exception as e:
return None
and add the following settings in your app
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'path_to_custom_authentication_backend',
....
)
}
now in each view/viewset you can access the user object with request.user
By reading the documentation on DRF Authentication and as #neverwalkaloner mentions in his comment, we see that we can access the logged-in user's django.contrib.auth.User instance in a view, by using the request.user attribute.
Reading the documentations of both the recommended JWT modules for DRF:
https://github.com/GetBlimp/django-rest-framework-jwt
https://github.com/davesque/django-rest-framework-simplejwt
I didn't find any evidence that they change/override the method of accesing the logged in user's instance info.
If you are familiar with django rest jwt, you may see a config like this in your settings.py:
JWT_AUTH = {
.....
'JWT_RESPONSE_PAYLOAD_HANDLER':
'rest_framework_jwt.utils.jwt_response_payload_handler',
'JWT_SECRET_KEY': SECRET_KEY,
....
}
You can simply create a method for example my_custom_jwt_response_payload_handler like below and address JWT_RESPONSE_PAYLOAD_HANDLER to new handler:
def jwt_response_payload_handler(token, user=None, request=None):
return {
'token': token,
'user': {
'username': user.username, 'id': user.id,
...
}
}
You can add any data that you want in this response. then patch settings.py with your new handler:
JWT_AUTH = {
.....
'JWT_RESPONSE_PAYLOAD_HANDLER':
'localtion-to-my-own-handler-file.my_custom_jwt_response_payload_handler',
....
}
For better understanding i suggest read original source and comments for jwt_response_payload_handler in here
Once you logged in, means you are authenticated by Django, now you can retrieve current user details anywhere in the code.
request.user
request contains all the details of the User Model once the user gets authenticated. otherwise, it will show the Anonymus user.
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)