I've used token authentication, and it's working fine i.e. it is authenticating a user and then the user is logged in. But in my views I've set permission classes to IsAuthenticated for one of the views, and it is not allowing to the user even if he is an authenticated user.
Below is the screenshot where it says i'm logged in (jadhav#gmail.com) :
and the very next tab to this, it says "authentication details not provided":
Can someone tell what's wrong?
ok, I'm providing details:
these are my settings:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
),
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated', )
}
This is how I authenticated:
class UserLoginAPIView(APIView):
permission_classes = [AllowAny]
serializer_class = UserLoginSerializer
def post(self, request, *args, **kwargs):
data = request.data
serializer = UserLoginSerializer(data=data)
if serializer.is_valid(raise_exception=True):
# new_data = serializer.data
if serializer.data:
user = authenticate(username=request.data['username'], password=request.data['password'])
login(request, user)
print("IsAuthenticated", user.is_authenticated)
token, _ = Token.objects.get_or_create(user=user)
return Response({'token': token.key},
status=HTTP_200_OK)
Another View where I put restrictions:
class BoardCreateAPIView(CreateAPIView):
queryset = Boards.objects.all()
serializer_class = BoardCreateSerializer
permission_classes = (IsAuthenticated,)
Just as #Reza hinted, you're missing the point of token authentication. You're trying to use the basic authentication flow instead. The flow is like this:
Client requests a token from the server using login and password
Server verifies that your credentials are correct, creates a token and returns it to the client
In subsequent requests, client adds the token to the Auth header like this:
Authorization: Token <the_client_token>
So what you should do in your login view is verify the user credentials and create a token. you shouldn't try to perform the authentication yourself. You can rename the view to obtain_token so as not to confuse its function.
Check the article #Reza linked for more info.
In django rest framework, You should provide token in your request headers. here is the sample with curl command:
curl -X POST -H "Content-Type: application/json" -H "Authorization: Token <MY_TOKEN>" http://my-api-url
Also check that in your settings.py at least you have these lines:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
)
}
For more understanding read this doc from django rest framework
Related
I have a Django REST App where I want to do the log in. I need the id of the currend logged in user and when I print it, it returns None. This is my code:
serializer.py
class LoginSerializer(serializers.Serializer):
username = serializers.CharField(
label="Username",
write_only=True
)
password = serializers.CharField(
label="Password",
# This will be used when the DRF browsable API is enabled
style={'input_type': 'password'},
trim_whitespace=False,
write_only=True
)
view.py
class LoginAPI(APIView):
def post(self, request):
username = request.data['username']
password = request.data['password']
user = User.objects.filter(username=username).first()
if user is None:
raise AuthenticationFailed('User not found!')
if not user.check_password(password):
raise AuthenticationFailed('Incorrect password!')
payload = {
'id': user.id,
}
token = jwt.encode(payload, 'secret', algorithm='HS256').decode('utf-8')
response = Response()
response.set_cookie(key='token', value=token, httponly=True)
response.data = {
'token': token
}
print(self.request.user.id)
return response
I don't know what I have to change in my log in view in order to print the id, not None.
You didn't login yet in that post function. So self.request.user does not exist.
print(user.id)
So i want to make a registration system with django rest framework so i can use it with React. Here is my serializers.py
from rest_framework import serializers
from backApp.models import Client
class RegistrationSerializer(serializers.ModelSerializer):
password2 = serializers.CharField(style={'input_type': 'password'}, write_only=True)
class Meta:
model = Client
fields = ['username', 'email', 'password', 'password2']
extra_kwargs = {
'password': {'write_only': True}
}
def save(self):
client = Client(
email = self.validated_data['email'],
username = self.validated_data['username'],
)
password = self.validated_data['password']
password2 = self.validated_data['password2']
if password != password2:
raise serializers.ValidationError({'password': "password doesn't match"})
client.set_password(password)
client.save()
return client
and here is views.py
from rest_framework import status
from rest_framework.response import Response
from rest_framework.decorators import api_view, permission_classes
from rest_framework import permissions
from authentication.api.serializers import RegistrationSerializer
#api_view(['POST',])
#permission_classes((permissions.AllowAny,))
def registration_view(request):
if request.method == "POST":
serializer = RegistrationSerializer(data=request.data)
data = {}
if serializer.is_valid():
client = serializer.save()
data['response'] = "successfully registred!"
data['email'] = client.email
data['username'] = client.username
else:
data = serializer.errors
return Response(data)
when i sent a post request with postman i get the following error:
password: field required
password2: field required
here is my input data
enter image description here
So please can you tell me where is the error and how i can fix it ?
Thanks in advance ^^.
You can not pass the password field using url, that I figured from your postman snapshot.
Take a look at this image you will find Body tab where you will be able to send data using postman and use post method. This should work for you.
You are making post request with params not form/json data. you will understand if you look at the url
I have an issue posting/getting any requests after JWT login. (I am using the djangorestframework-jwt library) The user succesfully logs in, the app returns the json token but when I use this token for the next requests I get
{
"detail": "Authentication credentials were not provided."
}
settings.py
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
),
}
JWT_AUTH = {
'JWT_ALLOW_REFRESH': True,
'JWT_EXPIRATION_DELTA': datetime.timedelta(hours=1),
'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=7),
}
Login View
def post(self, request, format=None):
if not request.data:
return Response({'Error': "Please provide username/password"}, status=status.HTTP_400_BAD_REQUEST)
username = request.data['username']
password = request.data['password']
try:
user = User.objects.get(email=username, password=password)
except User.DoesNotExist:
return Response({'Error': "Invalid username/password"}, status=status.HTTP_400_BAD_REQUEST)
if user:
payload = {
'id': user.id,
'email': user.email,
'first_name': user.first_name
}
jwt_token = jwt.encode(payload, "SECRET_KEY") # to be changed
return Response({'token': jwt_token}, status=status.HTTP_200_OK)
and then every other view contains
authentication_class = (JSONWebTokenAuthentication,)
permission_classes = (IsAuthenticated,)
I have tested both with postman and curl but I get the same error. Not sure if there is an error with the header format or if there is smth else I'm missing. Any help would be greatly appreciated!
EDIT:
I have changed my settings to
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
),
but now I get that
{
"detail": "Error decoding signature."
}
EDIT: I think the issue is that jwt_token = jwt.encode(payload, 'SECRET_KEY') might return a token that is not recognised...if i use the token generated by obtain_jwt_token then i can query any endpoint. Could anyone explain this?
EDIT: so I changed to jwt_token = jwt_encode_handler(payload) and the settings file contains the JWT_SECRET_KEY (when i verify the token i receive after login on jwt, it is indeed the right token with the right payload and secret) but it's still not recognised "detail": "Invalid signature."
I managed to solve my problem. When authenticating a user I was checking against a custom user table that I had created earlier, which was different from the django's auth_user table. I changed django's settings to use my custom users table and then using the token from the authentication worked for the other requests as well.
AUTH_USER_MODEL = 'main.User'
The problem is you encode the JWT by using "SECRET KEY" and decode using another key. The default secret key used by jwt is settings.SECRET_KEY. But when you create a custom jwt token you uses a string as secret "SECRET_KEY". But you use default jwt verification which automatically uses settings.SECRET_KEY.
So add 'JWT_SECRET_KEY': settings.SECRET_KEY, in to your JWT settings and use this key for encode and decode.
Ex:
jwt_token = jwt.encode(payload, settings.SECRET_KEY)
or you can override the jwt verification and create your own.
First time dealing with a SPA. I have a back-end restful service that returns a token when a user signs in. I know I am supposed to send the token through the headers in each request so I was thinking in saving the token in a file and create a service or a class that loads the token in every component but I don't know if this is a good approach as I can't find documentation for Angular Dart about this.
I saved the Token first in localStorage as Tobe O suggested:
Future login(username, password) async {
String url = 'http://127.0.0.1:8000/auth/login/';
var response =
await _client.post(url, body: {'username': username, 'password': password});
Map mapped_response = _decoder.convert(response.body);
window.localStorage.addAll({"token": mapped_response["key"]});
}
But still I was receiving 401 responses when I tried to get user information, this was the function:
Future check_authentification () async {
String _headers_key = "Authorization";
String _headers_value = "Token "+window.localStorage["token"];
var response = await _client.get("http://127.0.0.1:8000/auth/user/", headers: {_headers_key: _headers_value});
user_data = _decoder.convert(response.body);
response_status = response.statusCode;
}
I couldn't get authorized because django-rest-auth wasn't properly configured for token authorization. The solution was to add TokenAuthentication to the default authentication classes in django settings.
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
)}
My goal is to override obtain_jwt_token in order to get more control over the return value of the procedure, and in the doc I found only a bizarre and sketchy info on how to do this:
Note that the default obtain_auth_token view explicitly uses JSON
requests and responses, rather than using default renderer and parser
classes in your settings. If you need a customized version of the
obtain_auth_token view, you can do so by overriding the
ObtainAuthToken view class, and using that in your url conf instead
As for now, my attempt looks like this:
urlpatterns = [
url(r'^api-token-auth/', my_customized_view),
]
class Foo(ObtainAuthToken):
def post(self):
# here goes my customized code
my_customized_view = Foo.as_view()
The odds are that my code looks quite silly, and I am just lost trying to google it. I have little experience in Djagno, so please help me with this !
I have just been going through the same journey for comprehension as I wished to return the user and also allow email or username login. The documentation is not entirely clear, but as described for auth token, you can do the same for JWT.
obtain_auth_token is to ObtainAuthToken, as obtain_jwt_token is to ObtainJSONWebToken.
This is my overwritten login method:
from rest_framework_jwt.settings import api_settings
from rest_framework_jwt.views import ObtainJSONWebToken
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
jwt_decode_handler = api_settings.JWT_DECODE_HANDLER
class LoginView(ObtainJSONWebToken):
def post(self, request, *args, **kwargs):
# by default attempts username / passsword combination
response = super(LoginView, self).post(request, *args, **kwargs)
# token = response.data['token'] # don't use this to prevent errors
# below will return null, but not an error, if not found :)
res = response.data
token = res.get('token')
# token ok, get user
if token:
user = jwt_decode_handler(token) # aleady json - don't serialize
else: # if none, try auth by email
req = request.data # try and find email in request
email = req.get('email')
password = req.get('password')
username = req.get('username')
if email is None or password is None:
return Response({'success': False,
'message': 'Missing or incorrect credentials',
'data': req},
status=status.HTTP_400_BAD_REQUEST)
# email exists in request, try to find user
try:
user = User.objects.get(email=email)
except:
return Response({'success': False,
'message': 'User not found',
'data': req},
status=status.HTTP_404_NOT_FOUND)
if not user.check_password(password):
return Response({'success': False,
'message': 'Incorrect password',
'data': req},
status=status.HTTP_403_FORBIDDEN)
# make token from user found by email
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
user = UserSerializer(user).data
return Response({'success': True,
'message': 'Successfully logged in',
'token': token,
'user': user},
status=status.HTTP_200_OK)
You can change the default to check by email only if you please by customising Django's auth model, but I was happy to have both options.
I started creating an api boilerplate. There is a requirements.txt file and a config.example.py file for anyone who wants to pull it down to view the rest.
https://github.com/garyburgmann/django-api-boilerplate
In views.py, file add the following code and customize as you want.
def jwt_response_payload_handler(token, user=None, request=None):
return {
'token': token,
'user': UserSerializer(user, context={'request': request}).data
}
Default is {'token': token}
Also In your settings.py file add
JWT_AUTH = {
'JWT_RESPONSE_PAYLOAD_HANDLER':
'api.user.views.jwt_response_payload_handler',
}
( 'api.user.views.jwt_response_payload_handler',
) is the path to your custom jwt_response_payload_handler
For more help, you can view here