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
Related
I am using Django REST Framework and following this tutorial for retrieving all users when admin user is authenticated.
Class-based APIView of Django REST Framework
I am using Postman to test and trying to retrieve the list of all users registered in the system.
At first I try to use my "User Login with Token" API in Postman to create the necessary token as shown below:
I copied the value of the "token" key and pasted it as the value of the "Authorization" key in the "Headers" section of "Get All Users" API in Postman as shown below. It is a GET request and I get the error "detail": "Authentication credentials were not provided." as the response.
Necessary code snippets are as follows:
views.py
class UserAccountListView(APIView):
"""
List of All Users in the System / Application
* Requires Token Authentication.
* Only Admin Users are able to access this view.
"""
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAdminUser, )
def get(self, request, format=None):
"""
Returns a List of All Users
"""
full_names = [user.full_name for user in UsersAccount.objects.all()]
return Response(full_names)
settings.py
REST_FRAMEWORK = {
# Use Django's standard `django.contrib.auth` permissions,
# or allow read-only access for unauthenticated users.
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
'rest_framework.permissions.IsAdminUser',
],
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework_simplejwt.authentication.JWTAuthentication',
'rest_framework.authentication.TokenAuthentication',
],
SIMPLE_JWT = {
"ACCESS_TOKEN_LIFETIME": timedelta(minutes=30),
"REFRESH_TOKEN_LIFETIME": timedelta(days=1),
"ROTATE_REFRESH_TOKENS": False,
"BLACKLIST_AFTER_ROTATION": False,
"UPDATE_LAST_LOGIN": True,
"ALGORITHM": "HS256",
"SIGNING_KEY": SECRET_KEY,
"VERIFYING_KEY": None,
"AUDIENCE": None,
"ISSUER": None,
"AUTH_HEADER_TYPES": ("Bearer", ),
"AUTH_HEADER_NAME": "HTTP_AUTHORIZATION",
"USER_ID_FIELD": "id",
"USER_ID_CLAIM": "user_id",
"AUTH_TOKEN_CLASSES": ("rest_framework_simplejwt.tokens.AccessToken", ),
"TOKEN_TYPE_CLAIM": "token_type",
"JTI_CLAIM": "jti",
"SLIDING_TOKEN_REFRESH_EXP_CLAIM": "refresh_exp",
"SLIDING_TOKEN_LIFETIME": timedelta(minutes=5),
"SLIDING_TOKEN_REFRESH_LIFETIME": timedelta(days=1),
}
urls.py
urlpatterns = [
path('', UsersAccountAPIOverview.as_view()),
path("all", UserAccountListView.as_view()),
path("register", UsersAccountRegistrationView.as_view()),
path("token", UserAccountTokenObtainPairView.as_view()),
path("token/refresh", TokenRefreshView.as_view()),
path("token/verify", TokenVerifyView.as_view()),
]
Looking forward for your kind support and help. If you need further information, I will provide you.
Thank you.
In your views.py remove the line:
authentication_classes = (TokenAuthentication, )
This is because in your settings.py file, the first line:
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework_simplejwt.authentication.JWTAuthentication',
...
],
mentions JWTAuthentication and not TokenAuthentication
You must add Bearer in postman :
"Authorization" : "Bearer [token]"
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 trying to do a simple login page with the Django Rest Framework and I keep getting a csrf token error. To get around this for now, I have appended the #csrf_exempt annotation on my login method which works but is unsecure.
This is my method:
#csrf_exempt
def login(request):
print(request.COOKIES)
username = request.POST.get('username')
password = request.POST.get('password')
print("username {} password {}".format(username, password))
user = authenticate(request, username=username, password=password)
group = None
if user is not None:
django_login(request, user)
request.session.set_expiry(0)
result = True
status = 200
else:
result = False
status = 401
data = {'result': result, 'username': username}
return HttpResponse(json.dumps(data), content_type="application/json", status=status)
My Rest Framework Settings:
REST_FRAMEWORK = {
'DATETIME_FORMAT': "%m/%d/%Y %H:%M:%S",
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
),
'DEFAULT_FILTER_BACKENDS': (
'rest_framework.filters.SearchFilter',
'django_filters.rest_framework.DjangoFilterBackend',
),
'EXCEPTION_HANDLER': 'common.custom_exception_handler.custom_exception_handler'
}
Without the csrf_exempt annotation, I get
Forbidden (CSRF token missing or incorrect.): /authentication/login
however, when I print the cookies I actually get a token in my cookie.
{'csrftoken': 'HZc8vPqoad...7eIvTzep', 'sessionid': 'n71c....g5c7'}
gets printed when I add the #csrf_exempt annotation back in.
In my angular code, I have also tried to attach the csrf token as a request header with 'X-CSRFToken' but I noticed two things
1) in my request, the X-CSRFToken i obtain from document.cookies is NOT the same as the token above. There are two different CSRF tokens - why?
If you notice, the X-CSRFToken header and the token in the cookie differ. And I receive the same CSRF token missing or incorrect.
2) Even if I remove the use of the JWT Authentication, It has no effect.
I have also tried to use the new XSRF Strategy replacement with the new Cookie strategy in my app.module like this:
{
provide: XSRFStrategy,
useValue: new CookieXSRFStrategy('csrftoken', 'X-CSRFToken')
}
But to no avail - I get the same issue described in point (1).
However, when i add the #csrf_exempt annotation back in and check the cookie in the request, The cookie in the image shows up!!
So my main question is: why is the DRF not able to read the token even though the csrf cookie is part of the request?
I'm using Django and django-oauth-toolkit to build a generic OAuth2 Authorization Server for Auth0. I plan to use the Django server to authenticate users to several different services, using Auth0 as an intermediary.
I have a view that is called after an application has authenticated, and I need that view to return the details of the currently logged-in user.
urls.py:
# Return current logged in user
(r'^user/current/?$',
'my_app.views.current_user.get_user',
{},
'current_user'),
views/current_user.py:
import json
from django.http import HttpResponse
from oauth2_provider.decorators import protected_resource
#protected_resource()
def get_user(request):
user = request.user
return HttpResponse(
json.dumps({
'username': user.username,
'email': user.email}),
content_type='application/json')
request.user is returning AnonymousUser, instead of the user that the token belongs to.
How can I access the Django user associated with a token issued by django-oauth-toolkit?
Turns out, if you load the correct middleware and authentication backend, like it says in the documentation, request.user returns the correct user.
AUTHENTICATION_BACKENDS = (
'oauth2_provider.backends.OAuth2Backend',
# Uncomment following if you want to access the admin
#'django.contrib.auth.backends.ModelBackend'
'...',
)
MIDDLEWARE_CLASSES = (
'...',
# If you use SessionAuthenticationMiddleware, be sure it appears before OAuth2TokenMiddleware.
# SessionAuthenticationMiddleware is NOT required for using django-oauth-toolkit.
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'oauth2_provider.middleware.OAuth2TokenMiddleware',
'...',
)
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