@@ -529,6 +529,84 @@ func testHelperRotateRefreshToken(x oauth2.InternalRegistry) func(t *testing.T)
529
529
assert .ErrorIs (t , err , fosite .ErrInactiveToken , "Token is no longer active because it was refreshed" )
530
530
})
531
531
532
+ t .Run ("Rotation works when access token is already pruned" , func (t * testing.T ) {
533
+ // Test both with and without grace period
534
+ testCases := []struct {
535
+ name string
536
+ configureGrace bool
537
+ expectTokenActive bool
538
+ }{
539
+ {
540
+ name : "with grace period" ,
541
+ configureGrace : true ,
542
+ expectTokenActive : true ,
543
+ },
544
+ {
545
+ name : "without grace period" ,
546
+ configureGrace : false ,
547
+ expectTokenActive : false ,
548
+ },
549
+ }
550
+
551
+ for _ , tc := range testCases {
552
+ t .Run (tc .name , func (t * testing.T ) {
553
+ if tc .configureGrace {
554
+ x .Config ().MustSet (ctx , config .KeyRefreshTokenRotationGracePeriod , "1s" )
555
+ } else {
556
+ x .Config ().Delete (ctx , config .KeyRefreshTokenRotationGracePeriod )
557
+ }
558
+ t .Cleanup (func () {
559
+ x .Config ().Delete (ctx , config .KeyRefreshTokenRotationGracePeriod )
560
+ })
561
+
562
+ m := x .OAuth2Storage ()
563
+ r := newDefaultRequest (uuid .New ())
564
+
565
+ // Create tokens
566
+ refreshTokenSession := fmt .Sprintf ("refresh_token_%s" , uuid .New ())
567
+ accessTokenSession := fmt .Sprintf ("access_token_%s" , uuid .New ())
568
+
569
+ // Create access token
570
+ err := m .CreateAccessTokenSession (ctx , accessTokenSession , & r )
571
+ require .NoError (t , err )
572
+
573
+ // Create refresh token linked to the access token
574
+ err = m .CreateRefreshTokenSession (ctx , refreshTokenSession , accessTokenSession , & r )
575
+ require .NoError (t , err )
576
+
577
+ // Verify tokens were created successfully
578
+ req , err := m .GetRefreshTokenSession (ctx , refreshTokenSession , nil )
579
+ require .NoError (t , err )
580
+ require .Equal (t , r .GetID (), req .GetID ())
581
+
582
+ req , err = m .GetAccessTokenSession (ctx , accessTokenSession , nil )
583
+ require .NoError (t , err )
584
+ require .Equal (t , r .GetID (), req .GetID ())
585
+
586
+ // Delete the access token (simulating it being pruned)
587
+ err = m .DeleteAccessTokenSession (ctx , accessTokenSession )
588
+ require .NoError (t , err )
589
+
590
+ // Verify access token is gone
591
+ _ , err = m .GetAccessTokenSession (ctx , accessTokenSession , nil )
592
+ assert .Error (t , err )
593
+
594
+ // Rotation should still work even though the access token is gone
595
+ err = m .RotateRefreshToken (ctx , r .GetID (), refreshTokenSession )
596
+ require .NoError (t , err )
597
+
598
+ // Check refresh token state based on grace period configuration
599
+ req , err = m .GetRefreshTokenSession (ctx , refreshTokenSession , nil )
600
+ if tc .expectTokenActive {
601
+ assert .NoError (t , err )
602
+ assert .Equal (t , r .GetID (), req .GetID ())
603
+ } else {
604
+ assert .ErrorIs (t , err , fosite .ErrInactiveToken , "Token should be inactive when no grace period is configured" )
605
+ }
606
+ })
607
+ }
608
+ })
609
+
532
610
t .Run ("refresh token is valid until the grace period has ended" , func (t * testing.T ) {
533
611
x .Config ().MustSet (ctx , config .KeyRefreshTokenRotationGracePeriod , "1s" )
534
612
0 commit comments