@@ -580,6 +580,54 @@ def test_refresh(self):
580580 content = json .loads (response .content .decode ("utf-8" ))
581581 self .assertTrue ("invalid_grant" in content .values ())
582582
583+ def test_refresh_with_grace_period (self ):
584+ """
585+ Request an access token using a refresh token
586+ """
587+ oauth2_settings .REFRESH_TOKEN_GRACE_PERIOD_SECONDS = 120
588+ self .client .login (username = "test_user" , password = "123456" )
589+ authorization_code = self .get_auth ()
590+
591+ token_request_data = {
592+ "grant_type" : "authorization_code" ,
593+ "code" : authorization_code ,
594+ "redirect_uri" : "http://example.org"
595+ }
596+ auth_headers = get_basic_auth_header (self .application .client_id , self .application .client_secret )
597+
598+ response = self .client .post (reverse ("oauth2_provider:token" ), data = token_request_data , ** auth_headers )
599+ content = json .loads (response .content .decode ("utf-8" ))
600+ self .assertTrue ("refresh_token" in content )
601+
602+ # make a second token request to be sure the previous refresh token remains valid, see #65
603+ authorization_code = self .get_auth ()
604+ token_request_data = {
605+ "grant_type" : "authorization_code" ,
606+ "code" : authorization_code ,
607+ "redirect_uri" : "http://example.org"
608+ }
609+ response = self .client .post (reverse ("oauth2_provider:token" ), data = token_request_data , ** auth_headers )
610+
611+ token_request_data = {
612+ "grant_type" : "refresh_token" ,
613+ "refresh_token" : content ["refresh_token" ],
614+ "scope" : content ["scope" ],
615+ }
616+ response = self .client .post (reverse ("oauth2_provider:token" ), data = token_request_data , ** auth_headers )
617+ self .assertEqual (response .status_code , 200 )
618+
619+ content = json .loads (response .content .decode ("utf-8" ))
620+ self .assertTrue ("access_token" in content )
621+ first_access_token = content ["access_token" ]
622+
623+ # check refresh token returns same data if used twice, see #497
624+ response = self .client .post (reverse ("oauth2_provider:token" ), data = token_request_data , ** auth_headers )
625+ self .assertEqual (response .status_code , 200 )
626+ content = json .loads (response .content .decode ("utf-8" ))
627+ self .assertTrue ("access_token" in content )
628+ self .assertEqual (content ["access_token" ], first_access_token )
629+ oauth2_settings .REFRESH_TOKEN_GRACE_PERIOD_SECONDS = 0
630+
583631 def test_refresh_invalidates_old_tokens (self ):
584632 """
585633 Ensure existing refresh tokens are cleaned up when issuing new ones
@@ -608,7 +656,8 @@ def test_refresh_invalidates_old_tokens(self):
608656 response = self .client .post (reverse ("oauth2_provider:token" ), data = token_request_data , ** auth_headers )
609657 self .assertEqual (response .status_code , 200 )
610658
611- self .assertFalse (RefreshToken .objects .filter (token = rt ).exists ())
659+ refresh_token = RefreshToken .objects .filter (token = rt ).first ()
660+ self .assertIsNotNone (refresh_token .revoked )
612661 self .assertFalse (AccessToken .objects .filter (token = at ).exists ())
613662
614663 def test_refresh_no_scopes (self ):
@@ -693,6 +742,46 @@ def test_refresh_fail_repeating_requests(self):
693742 response = self .client .post (reverse ("oauth2_provider:token" ), data = token_request_data , ** auth_headers )
694743 self .assertEqual (response .status_code , 401 )
695744
745+ def test_refresh_repeating_requests (self ):
746+ """
747+ Trying to refresh an access token with the same refresh token more than
748+ once succeeds in the grace period and fails outside
749+ """
750+ oauth2_settings .REFRESH_TOKEN_GRACE_PERIOD_SECONDS = 120
751+ self .client .login (username = "test_user" , password = "123456" )
752+ authorization_code = self .get_auth ()
753+
754+ token_request_data = {
755+ "grant_type" : "authorization_code" ,
756+ "code" : authorization_code ,
757+ "redirect_uri" : "http://example.org"
758+ }
759+ auth_headers = get_basic_auth_header (self .application .client_id , self .application .client_secret )
760+
761+ response = self .client .post (reverse ("oauth2_provider:token" ), data = token_request_data , ** auth_headers )
762+ content = json .loads (response .content .decode ("utf-8" ))
763+ self .assertTrue ("refresh_token" in content )
764+
765+ token_request_data = {
766+ "grant_type" : "refresh_token" ,
767+ "refresh_token" : content ["refresh_token" ],
768+ "scope" : content ["scope" ],
769+ }
770+ response = self .client .post (reverse ("oauth2_provider:token" ), data = token_request_data , ** auth_headers )
771+ self .assertEqual (response .status_code , 200 )
772+ response = self .client .post (reverse ("oauth2_provider:token" ), data = token_request_data , ** auth_headers )
773+ self .assertEqual (response .status_code , 200 )
774+
775+ # try refreshing outside the refresh window, see #497
776+ rt = RefreshToken .objects .get (token = content ["refresh_token" ])
777+ self .assertIsNotNone (rt .revoked )
778+ rt .revoked = timezone .now () - datetime .timedelta (minutes = 10 ) # instead of mocking out datetime
779+ rt .save ()
780+
781+ response = self .client .post (reverse ("oauth2_provider:token" ), data = token_request_data , ** auth_headers )
782+ self .assertEqual (response .status_code , 401 )
783+ oauth2_settings .REFRESH_TOKEN_GRACE_PERIOD_SECONDS = 0
784+
696785 def test_refresh_repeating_requests_non_rotating_tokens (self ):
697786 """
698787 Try refreshing an access token with the same refresh token more than once when not rotating tokens.
0 commit comments