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
Related
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 = ([])
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 having issues with DRF's token based authentication. Following is my landing page code (after login):
#api_view(['GET','POST'],)
def landing(request):
this_tenant=request.user.tenant
end=date_first.date.today()
start=end-date_first.timedelta(days=30)
sales_daily=sales_day_wise(start, end, this_tenant)
invoice_value=sales_raised_value(start, end, this_tenant)
payment_value=sales_collected_value(start, end, this_tenant)
return render(request,'landing.html', {'sales_daily':json.dumps(sales_daily, cls=DjangoJSONEncoder),\
'invoice_value':json.dumps(invoice_value, cls=DjangoJSONEncoder), \
'payment_value':json.dumps(payment_value, cls=DjangoJSONEncoder)})
I was using Django's built-in login view to authenticate and log in a user, then I revised to consider putting token in the header. But that is also not working
This is my login code:
#Redirect authenticated users to landing page
def custom_login(request):
if request.user.is_authenticated():
token, created = Token.objects.get_or_create(user=request.user)
request.session['AUTHORIZATION'] = "TOKEN "+token.key
return redirect(landing)
else:
return login(request)
Following is my DRF settings:
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.JSONRenderer',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
# 'rest_framework.authentication.SessionAuthentication',
),
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
}
Issue is, when I'm logging in and going to the landing page via browser, DRF is not working and I'm getting the following error:
{"detail":"Authentication credentials were not provided."}
The reason is the custom DRF header (AUTHENTICATION = TOEKN XXXXXXXXXX) is not present in the request.
However, if I use Postman and put in the custom header (AUTHENTICATION = TOKEN XXXXXXXXXXXX), then it works.
How do I solve it?
Would this mean I would need a custom header for every view?
And on using DRF Token, does it open up CSRF vulnerability (this question: Django DRF - How to do CSRF validation with token authentication )?
Thanks a lot!!
How do I solve it? Would this mean I would need a custom header for every view?
TokenAuthentication is used for Single Page App and the token in Request Header needs to be provided by API client(Postman, Javascript or any other client) on each request. In your case, if you want to use Django views you should activate SessionAuthentication. TokenAuthentication and SessionAuthentication can co-exist.
One way is to save the token in the cookie in your in custom login view and read it by javascript client.
On using DRF Token, does it open up CSRF vulnerability (this question: Django DRF - How to do CSRF validation with token authentication )?
Yes. But there are ways to secure the requests.
According to DRF documentation
"If you use token authentication in production you must ensure that your API is only available over https".
Also, make sure set ALLOWED_HOSTS in settings to the domains that you want Django to respond to so the server does not respond to requests with other origins. There other more secure Auths can be used beside TokenAuthentication like JWT that has been mentioned above.
You need to learn the basics first. What is HTTP, what is HTTP headers, what is Django session (it's not an HTTP header and contents of the session doesn't affect the headers), read Django REST Framework documentation on token authentication.
If you want to test your views in browsers, then explicitly allow Django Session authentication in the DRF DEFAULT_AUTHENTICATION_CLASSES configuration variable. It can coexist with token authentication.
You can't make plain web browser append token to the HTTP request unless you're using some plugin like RESTClient, or DHC or REST Easy.
You're adding token to the Django session but you have disabled session authentication in DRF and even if you'd enable it, DRF doesn't read token from Django session because API client has no way to add Token to the Django session. Even if DRF would read Token from Django sessions it would be totally pointless as the client has no control over contents of the session. Session variables are set on the server, not on the client.
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
],
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication', # enables simple command line authentication
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
)
}
Adding 'rest_framework.authentication.SessionAuthentication' has solved this issue most of the time.
Alternative
You can use:
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
),
But now, in order to access protected api urls you must include the Authorization: JWT header.
If it works with curl or postman, this indicates it is not an issue with the backend. This is certainly an issue with the client side code. Have you had a look at your request to your REST api? I would recommend that and ensure the token is being passed in the headers and is formatted correctly.