I want to send the SimpleJWT
access
and refresh
tokens through HttpOnly
cookie. I have customized the claim. I have defined a post()
method in the MyObtainTokenPairView(TokenObtainPairView)
in which I am setting the cookie. This is my code:
from .models import CustomUser
class MyObtainTokenPairView(TokenObtainPairView):
permission_classes = (permissions.AllowAny,)
serializer_class = MyTokenObtainPairSerializer
def post(self, request, *args, **kwargs):
serializer = self.serializer_class()
response = Response()
tokens = serializer.get_token(CustomUser)
access = tokens.access
response.set_cookie('token', access, httponly=True)
return response
It's returning this error:
AttributeError: 'RefreshToken' object has no attribute 'access'
The serializer:
class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
@classmethod
def get_token(cls, user):
print(type(user))
token = super().get_token(user)
token['email'] = user.email
return token
But it's just not working. I think I should not define a post()
method here like this. I think if I can only return the value of the get_token()
function in the serializer, I could set it as HttpOnly
cookie. But, I don't know how to do that.
How do I set the access
and refresh
tokens in the HttpOnly
cookie?
EDIT: I made these changes following anowlinorbit's answer:
I changed my serializer to this:
class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
def validate(self, attrs):
attrs = super().validate(attrs)
token = self.get_token(self.user)
token["email"] = self.user.email
return token
Since this token contains the refresh token by default therefore, I decided that returning only this token
would provide both access
and refresh
token.
If I add anything like token["access"] = str(token.access_token)
it would just add the access token string inside the refresh token string, which it already contains.
But again in the view, I could not find how to get the refresh token. I could not get it using serializer.validated_data.get('refresh', None)
since now I am returning the token
from serializer which contains everything.
I changed my view to this:
class MyObtainTokenPairView(TokenObtainPairView):
permission_classes = (permissions.AllowAny,)
serializer_class = MyTokenObtainPairSerializer
def post(self, request, *args, **kwargs):
response = super().post(request, *args, **kwargs)
response.set_cookie('token', token, httponly=True)
return response
Now it's saying:
NameError: name 'token' is not defined
What's wrong here? In the view I want to get the token
returned from serializer, then get the acces token using token.access_token
and set both refresh
and access
as cookies.
I would leave .get_token()
alone and instead focus on .validate()
. In your MyTokenObtainPairSerializer
I would remove your changes to .get_token()
and add the following
def validate(self, attrs):
data = super().validate(attrs)
refresh = self.get_token(self.user)
data["refresh"] = str(refresh) # comment out if you don't want this
data["access"] = str(refresh.access_token)
data["email"] = self.user.email
""" Add extra responses here should you wish
data["userid"] = self.user.id
data["my_favourite_bird"] = "Jack Snipe"
"""
return data
It is by using the .validate()
method with which you can choose which data you wish to return from the serializer object's validated_data
attribute. N.B. I have also included the refresh token in the data which the serializer returns. Having both a refresh and access token is important. If a user doesn't have the refresh token they will have to login again when the access token expires. The refresh token allows them to get a new access token without having to login again.
If for whatever reason you don't want the refresh token, remove it from your validate()
serializer method and adjust the view accordingly.
In this post method, we validate the serializer and access its validated data.
def post(self, request, *args, **kwargs):
# you need to instantiate the serializer with the request data
serializer = self.serializer(data=request.data)
# you must call .is_valid() before accessing validated_data
serializer.is_valid(raise_exception=True)
# get access and refresh tokens to do what you like with
access = serializer.validated_data.get("access", None)
refresh = serializer.validated_data.get("refresh", None)
email = serializer.validated_data.get("email", None)
# build your response and set cookie
if access is not None:
response = Response({"access": access, "refresh": refresh, "email": email}, status=200)
response.set_cookie('token', access, httponly=True)
response.set_cookie('refresh', refresh, httponly=True)
response.set_cookie('email', email, httponly=True)
return response
return Response({"Error": "Something went wrong", status=400)
If you didn't want the refresh token, you would remove the line beginning refresh =
and remove the line where you add the refresh
cookie.