Opened 9 months ago

Closed 9 months ago

#35340 closed Uncategorized (invalid)

Why does Django add 1 second to the lifetime of a cookie?

Reported by: Leonid Owned by: nobody
Component: HTTP handling Version: 5.0
Severity: Normal Keywords: cookies
Cc: Leonid Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

I'm trying to implement JWT authentication using django-graphql-jwt on the backend, and nuxt3 for the frontend. My token and refresh_token are stored in cookies, and I wrapped the GraphQLView in a jwt_cookie decorator that automatically checks for the existence of the jwt token in the cookie, as well as its validity.
In general, everything works great. When I send an authorization request, the backend returns me the "Set-Cookie" headers for JWT and JWT_REFRESH_TOKEN with an expiration date that automatically deletes them as soon as they become invalid, which saves me a lot of code/logic on the frontend.

However, there is one very unpleasant thing - Django adds 1 second to the cookie lifetime for some reason in the set_cookie function, and it ruins everything. Here is the Django code:

    def set_cookie(
        self,
        key,
        value="",
        max_age=None,
        expires=None,
        path="/",
        domain=None,
        secure=False,
        httponly=False,
        samesite=None,
    ):
        self.cookies[key] = value
        if expires is not None:
            if isinstance(expires, datetime.datetime):
                if timezone.is_naive(expires):
                    expires = timezone.make_aware(expires, datetime.timezone.utc)
                delta = expires - datetime.datetime.now(tz=datetime.timezone.utc)
                # Add one second so the date matches exactly (a fraction of
                # time gets lost between converting to a timedelta and
                # then the date string).
                
                # Here's what I mean ↓↓↓
                delta += datetime.timedelta(seconds=1)


                # Just set max_age - the max_age logic will set expires.
                expires = None
                if max_age is not None:
                    raise ValueError("'expires' and 'max_age' can't be used together.")
                max_age = max(0, delta.days * 86400 + delta.seconds)
            else:
                self.cookies[key]["expires"] = expires
        else:
        ...

I see the comment from Django, but I still don't understand what it means.

This may not be a critical issue, but I just can't understand why they did it. The result of this behavior is that within 1 second after the token has expired, it is still in the user's cookies, and that doesn't seem right to me.
I can fix this problem by subtracting 1 second when creating cookies, but I would like to avoid unpredictable errors that may arise due to this innovation.

I would appreciate your answers and explanations :)

Change History (1)

comment:1 by David Sanders, 9 months ago

Resolution: invalid
Status: newclosed

Issue trackers are for reporting bugs, please don't seek assistance here. You can get help from friendly community members on Discord or the forum 👍 https://www.djangoproject.com/community/

Note: See TracTickets for help on using tickets.
Back to Top