I have someViews like below:
class SomeView(generics.ListAPIView):
serializer_class = SomeSerializer
permission_classes = [AllowAny]
settings.py:
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.AllowAny',
),
...
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication',
),
...
}
And When I request without any Authorization header it works fine.
But When I add Bearer Authorization header it response
"detail": "Given token not valid for any token type",
"code": "token_not_valid",
I gave permission_classes=[AllowAny].
Why? I thought there is no difference between sending or not sending tokens. Because I set permission_class=[AllowAny].
In ASP.NET there is no like this problems. In ASP.NET If I set AllowAny permission
this endpoint open for everyone regardless of whether you send a Token or not.
EDIT:
And When I request without or with any Authorization header it works fine.
But When I add wrong Authorization header it gives authorization error
AllowAny verify token if it sent and pass if not any. So, if you dont need to authenticat, clear permission_classes
permission_classes = ([])
Related
I am using the Django Restful API Framework together with Simple JWT and have successfully created a URL for receiving and refreshing a user token.
In order to try out the authentication using the token, I have created a view that simply lists all the posts inside the database. I have then assigned the IsAuthenticated class to the view.
As expected, I get an error message saying that the authentication credentials were not provided. I then went ahead and made a simple GET request using Postman, with the authentication token provided in the "Authorization" tab. The type was set to "Bear Token". Unfortunately, I still get the message "Authentication credentials were not provided." with a 403 Forbidden code.
I have also tried to provide the token in the Headers, as well as make CURL requests, everything to no avail.
My view looks like this:
class PostListView(generics.ListAPIView):
permission_classes = (IsAuthenticated,)
queryset = Post.objects.filter()
This is the serializer:
class PostListSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ('__all__')
The settings.py of the Django project:
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'],
'DEFAULT_PERMISSION_CLASSES': ['rest_framework.permissions.AllowAny'],
'DEFAULT_AUTHENTICATION_CLASSES:': ('rest_framework_simplejwt.authentication.JWTAuthentication',)
}
CORS_ALLOW_ALL_ORIGINS = True # For testing purposes
I have followed several different tutorials online, read through numerous posts as well as followed the official documentation of Simple JWT.
Well what you are doing is trying to filter the data while your basic purpose is to just list your model. For filtering make sure your go through the documentation DRF Filtering.
Try these changes in your code. I hope it will work for you.
Settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework_simplejwt.authentication.JWTAuthentication',
],
}
Views.py
class UserList(generics.ListAPIView):
permission_classes = (IsAuthenticated,)
queryset = Post.objects.all()
serializer_class = PostListSerializer
After this try to hit your API with access token. To learn more about generic views you can go through this link Generic Views in DRF.
I currently have Django basic auth setup with Knox token authentication. Basic Auth doesn't seem sufficient for production work, so I want to replace that. Does Django have another password-based authentication_class that I can easily replace BasicAuthentication with, or is this a more involved process? If so, where do I start?
my login api view:
class UserLoginView(GenericAPIView):
serializer_class = UserOrganizationSerializer
authentication_classes = (BasicAuthentication,)
permission_classes = (IsAuthenticated,)
def post(self, request):
"""User login with username and password."""
token = AuthToken.objects.create(request.user)
return Response({
'user': self.get_serializer(request.user).data,
'token': token
})
my default authentication classes:
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [],
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
],
One very common way is to use a JSON Web Token (JWT). The basic package is django-rest-framework-jwt. The instructions are pretty clear in the documentation, but here is an overview:
$> pip install djangorestframework-jwt
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
...
),
Add the URL patterns
url_patterns = [
path('jwt/', obtain_jwt_token, name='jwt'),
...
]
Get a token (using the excellent httpie utility)
http post localhost:8000/api/jwt/ username=u password=p
{
"token": "REALLY-LONG-TOKEN"
}
Use that token to make a request:
http get localhost:8000/api/some-endpoint/ Authorization:"JWT REALLY-LONG_TOKEN"
Some Notes
JWT tokens are meant to be decodable by the client. They are protected by a signature, so they can't be modified
You can decode the token (online) and see the expiration time & other data
Tokens can be refreshed or verified through other urls provided by the package
Eventually refreshing will fail and the user will need to login again. This timespan is configurable (see my response to this other question)
I am using JWT authentication for my django-rest-framework and react project. So, I have defined a URL path that provides the JWT token.
path('api/auth/token/', obtain_jwt_token),
I have defined another path which retrieves the current logged-in user:
path('current_user/', current_user, name='current-user'),
current_user:
#api_view(['GET'])
def current_user(request):
if not request.user.is_authenticated:
return Response('User is not authenticated')
profile = Profile.objects.get(user=request.user)
serializer = CurrentProfileSerializer(profile)
return Response(serializer.data)
The problem is, after I log in at api/auth/token/ and then go to current_user/, I am getting 'User is not authenticated' response. I thought that obtain_jwt_token returns a token and logs in the user to request.user. Am I wrong for assuming this? Please ask if I need to provide any more details.
settings.py
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticatedOrReadOnly',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
),
}
when you receive token in api/auth/token reuqest, you should store it in frontend. then in currect_user request, use this stored token in header of request. like this:
Authorization: JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoyLCJ1c2VybmFtZSI6ImFsaUBtYWlsLmNvbSIsImV4cCI6MTUzMzcxNjUzNCwiZW1haWwiOiJhbGlAbWFpbC5jb20ifQWfVfp6Nfj9gvedTkqhqlwZhAwzi2YK64cx2FpRLms
I'm writing tests to check if the Authenticated users have access to the API Endpoints.
On my test settings, I have set the defaults for Rest Framework Authentication and Permissions Classes. The default setting is that everyone has to be authenticated to access the API.
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
),
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
)}
This is the function which is failing (and all others). Here, I create a user object with a custom UserFactory which is setting a default email and password for each user created. Then I use the APIClient with basic authentication to log in. I'm following the official Django Rest Framework Documentation
def test_retrieve(self):
user = UserFactory.create()
client = APIClient()
client.login(email=user.email, password=user.password)
entity = AttributeChoiceFactory.create()
response = self.get(retrieve=entity.pk)
self.assertEqual(response.status_code, status.HTTP_200_OK)
item = json.loads(response.content)
self.assertEqual(type(item), dict)
self.assertEqual(item['pk'], entity.pk)
self.assertItemsEqual(AttributeChoiceSerializer.Meta.fields, item.keys())
The test fails with Not Authorized Status Code AssertionError: 401 != 200
The problem lies on this line: response = self.get(retrieve=entity.pk)
Since you are using client to login, you must continue to use it to send requests: response = client.get(retrieve=entity.pk)
I'm following the DRF docs to setup TokenAuthentication, and can't get it working with the browsable API. I believe I've added the proper lines in settings.py:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
),
INSTALLED_APPS = (
...
'rest_framework',
'rest_framework.authtoken',
...
As well as generated tokens for existing users with the code snippet from the docs. I can see tokens for each user if I query the authtoken_token table, so I know they exist.
Everytime I try to log in to the browsable API, I get the following content returned:
HTTP 401 Unauthorized
Allow: GET, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
WWW-Authenticate: Token
{
"detail": "Authentication credentials were not provided."
}
So it appears to be attempting Token authentication, but this message is a little odd. When I enter an incorrect password, I get the 'enter a correct password' message on the login form. When I enter the correct password, it appears to login, but takes me to the API root with the above message, and displays "Log In" on the top menu, rather than the username.
Could this be related to my custom user model somehow? Or could it be due to the fact that I'm currently developing with the dev server, which doesn't support https- the DRF docs mention needing HTTPS with TokenAuthentication, though I wasn't sure if that was a best practice or actually required.
You can't use the browsable api with TokenAuthentication. You have to add SessionAuthtication to your settings (http://www.django-rest-framework.org/api-guide/authentication/#sessionauthentication):
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
),
You can use a browser plugin to set token in the header. I'm using Modheader which is free.
The example of setting the header:
I wrote a blog post on how this can be done: link to post.
I like this solution because you don't need to change the auth classes.
I did:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
),
and I added a custom auth class in api.py
class CustomAuthToken(ObtainAuthToken):
authentication_classes = [TokenAuthentication]
def post(self, request, *args, **kwargs):
...
return Response({...})
See https://www.django-rest-framework.org/api-guide/authentication/#by-exposing-an-api-endpoint