Skip to content

Able to refresh a token after it has been revoked #1510

@dwaynelaforce

Description

@dwaynelaforce

Describe the bug

I am able to exchange a refresh token for a new access token when the original access token was revoked. I had a test in my local django project that validated this did not happen (in version 2.3.0) but now it does (in version 3.0.1). I want to know if this is desired behavior or perhaps a regression has been introduced to the codebase.

To Reproduce

Create an Application with Authorization Code type.
Initiate the workflow from the external app, authenticate, and authorize.
Go to the authorized tokens page, select the new token, and delete.
In postman, make a refresh token request using the refresh token, the response is 200 OK and includes a new access token and refresh token.

Expected behavior

After revoking an access token, the corresponding refresh token would also be revoked and unusable by the external application.

Version

3.0.1

  • I have tested with the latest published release and it's still a problem.
    I have tested with the master branch and it's still a problem.

Additional context

Sorry if this has been raised already- I did browse the open and closed issues for this release and did not see anything. Thanks for all your work on this package.

Activity

n2ygk

n2ygk commented on Oct 2, 2024

@n2ygk
Contributor

Related to #1452? @soerface can you please take a look at this?

soerface

soerface commented on Oct 2, 2024

@soerface
Contributor

@dwaynelaforce do you use the REFRESH_TOKEN_GRACE_PERIOD_SECONDS setting? If yes, I'm wondering if the refresh token is still valid for the specified amount of time?

dwaynelaforce

dwaynelaforce commented on Nov 4, 2024

@dwaynelaforce
Author

@soerface sorry for taking so long to respond! priorities shifted at work for the last month but I am back on this now.

We are not using that setting but we do use REFRESH_TOKEN_EXPIRE_SECONDS which is set to 7 days.

Here is the django test code in our django app that was working in the prior version but is failing in this version:

    def test_refresh_token_cannot_be_used_on_a_manually_revoked_access_token(self):
        access_token = self._create_accesstoken(self.user)
        refreshtoken = self._create_refreshtoken(access_token)

        session_client = Client()
        session_client.force_login(self.user)
        resp = session_client.post(f"/o/authorized_tokens/{access_token.id}/delete/")
        self.assertEqual(resp.status_code, 302)
        self.assertFalse(AccessToken.objects.filter(token=access_token.token).exists())
        refreshtoken.refresh_from_db()
        self.assertIsNone(refreshtoken.access_token)

        self.assertRaises(
            AccessToken.DoesNotExist,
            Client().post,
            self.token_endpoint,
            data={
                "client_id": self.client_id,
                "client_secret": self.client_secret,
                "refresh_token": refreshtoken.token,
                "grant_type": "refresh_token",
            },
        )

I have verified that the response from that API call in the assertRaises is 200 OK and includes a new access token and refresh token.

dwaynelaforce

dwaynelaforce commented on Nov 4, 2024

@dwaynelaforce
Author

After more closely reviewing oauth2_provider/models.py and oauth2_provider/views/token.py I notice that when a refresh token is revoked, it also revokes its related access token, while the reverse is not true- the access token simply deletes itself. The authorized-tokens and delete views are only for access tokens and appear to call delete() rather than revoke(). Not sure why my test was working in the previous version though.

I'm guessing then that this is intended behavior and the issue here is my expectation being different. Could you confirm?

If the case, it sounds like the correct way for a user to revoke third party access to their account is to revoke the refresh token if it exists rather than the access token. Is a new template needed, where a user's access tokens AND refresh tokens are provided with links to revoke?

dopry

dopry commented on Nov 1, 2025

@dopry
Member

Token revokation is specified in https://datatracker.ietf.org/doc/html/rfc7009#section-2.1

Depending on the authorization server's revocation policy, the revocation of a particular token may cause the revocation of related tokens and the underlying authorization grant. If the particular token is a refresh token and the authorization server supports the revocation of access tokens, then the authorization server SHOULD also invalidate all access tokens based on the same authorization grant (see Implementation Note). If the token passed to the request is an access token, the server MAY revoke the respective refresh token as well.

At this juncture, given the implemented behavior in DOT, I'd say yes revoke the refresh token. Based on your asserts DOT made the choice not to revoke refresh tokens when an access token is revoked per the permissive MAY in If the token passed to the request is an access token, the server MAY revoke the respective refresh token as well

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @dopry@soerface@n2ygk@dwaynelaforce

        Issue actions

          Able to refresh a token after it has been revoked · Issue #1510 · django-oauth/django-oauth-toolkit