Reputation: 515
I'm setting up Django to send a JWT Response as opposed to a view. I tried using django-rest-framework-simplejwt.
Provided in this framework, there is a function TokenObtainPairView.as_view()
that returns a pair of jwt. I need to return the access token with another Json response as opposed to the two tokens provided.
Ideally I would like one JsonResponse that contains an access token that is the same as this one: TokenObtainPairView.as_view()
.
I tried creating my own view which is provided below.
UPDATE: Provided in Settings.py
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(days=1),
'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
'ROTATE_REFRESH_TOKENS': False,
'BLACKLIST_AFTER_ROTATION': True,
'ALGORITHM': 'HS256',
'SIGNING_KEY': SECRET_KEY,
'VERIFYING_KEY': None,
'AUTH_HEADER_TYPES': ('Bearer',),
'USER_ID_FIELD': 'id',
'USER_ID_CLAIM': 'user_id',
'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
'TOKEN_TYPE_CLAIM': 'token_type',
'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp',
'SLIDING_TOKEN_LIFETIME': timedelta(days=1),
'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=1),
}
Login URL Path
urlpatterns = [
path('auth/', views.LoginView.as_view()),
]
LoginView I created
class LoginView(APIView):
permission_classes = (AllowAny,)
def post(self, request, *args, **kwargs):
username = request.data['username']
password = request.data['password']
user = authenticate(username=username, password=password)
if user is not None:
payload = {
'user_id': user.id,
'exp': datetime.now(),
'token_type': 'access'
}
user = {
'user': username,
'email': user.email,
'time': datetime.now().time(),
'userType': 10
}
token = jwt.encode(payload, SECRET_KEY).decode('utf-8')
return JsonResponse({'success': 'true', 'token': token, 'user': user})
else:
return JsonResponse({'success': 'false', 'msg': 'The credentials provided are invalid.'})
Pattern provided by framework.
urlpatterns = [
...
path('token/', TokenObtainPairView.as_view()),
...
]
It returns this token
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNTQ5NDk3NDQ2LCJqdGkiOiI3YmU4YzkzODE4MWI0MmJlYTFjNDUyNDhkNDZmMzUxYSIsInVzZXJfaWQiOiIwIn0.xvfdrWf26g4FZL2zx3nJPi7tjU6QxPyBjq-vh1fT0Xs
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTU0OTQ5NzQ0NiwianRpIjoiOTNhYzkxMjU5NmZkNDYzYjg2OGQ0ZTM2ZjZkMmJhODciLCJ1c2VyX2lkIjoiMCJ9.dOuyuFuMjkVIRI2_UcXT8_alCjlXNaiRJx8ehQDIBCg
If you go to https://jwt.io/ you will see what's returned
Upvotes: 26
Views: 25606
Reputation: 5528
validate
method in TokenObtainPairSerializer
# project/views.py
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from rest_framework_simplejwt.views import TokenObtainPairView
class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
def validate(self, attrs):
data = super().validate(attrs)
# Add your extra responses here
data['username'] = self.user.username
data['groups'] = self.user.groups.values_list('name', flat=True)
return data
class MyTokenObtainPairView(TokenObtainPairView):
serializer_class = MyTokenObtainPairSerializer
# project/urls.py
from .views import MyTokenObtainPairView
urlpatterns = [
# path('token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
path('token/', MyTokenObtainPairView.as_view(), name='token_obtain_pair'),
]
🔚
References: SimpleJWT Readme and the source code as below:
Upvotes: 58
Reputation: 1
I had the same issue this can be resolved by something like this if you want to handle the custom response in case of an invalid credential.
# JWT View
class LoginView(TokenObtainPairView):
'''
Returns access token and refresh token on login
'''
permission_classes = (AllowAny,)
serializer_class = MyTokenObtainPairSerializer
def post(self, request, *args, **kwargs):
try:
response = super().post(request, *args, **kwargs)
return response
except exceptions.AuthenticationFailed:
return Response({
'error': True,
'message': 'Invalid Username or Password',
}, status=status.HTTP_401_UNAUTHORIZED)
Upvotes: 0
Reputation: 2240
Custom Token response like this:
from rest_framework_simplejwt.tokens import RefreshToken
class LoginView(APIView):
permission_classes = (AllowAny,)
def post(self, request, *args, **kwargs):
# ...
# Get user token, include refresh and access token
token = RefreshToken.for_user(user)
# Serializer token if it is not correct JSON format ^^
return JsonResponse({'success': 'true', 'token': token, 'user': user})
Upvotes: 3
Reputation: 3331
A very clean approach from the django-rest-framework-simplejwt README as below
For example, I have users app where my custom User model resides. Follow serializers and views code example below.
users/serializers.py:
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
@classmethod
def get_token(cls, user):
token = super().get_token(user)
# Add custom claims
token['name'] = user.name
# Add more custom fields from your custom user model, If you have a
# custom user model.
# ...
return token
users/views.py:
from rest_framework_simplejwt.views import TokenObtainPairView
class MyTokenObtainPairView(TokenObtainPairView):
serializer_class = MyTokenObtainPairSerializer
After that, Register above View in your project's urls.py
replacing original TokenObtainPairView
as below.
from users.views import MyTokenObtainPairView
urlpatterns = [
..
# Simple JWT token urls
# path('api/token/', TokenObtainPairView.as_view(),
# name='token_obtain_pair'),
path('api/token/', CustomTokenObtainPairView.as_view(),
name='token_obtain_pair')
..
]
Now get access token and decode it at jwt.io
Upvotes: 12