mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Auth: Return error when retries have been exhausted for OAuth token refresh (#98034)
Return error when retries for DB lock have been exhausted in oauth_token.go
This commit is contained in:
parent
b310b6f8c5
commit
6cd3a5458e
@ -98,6 +98,11 @@ func (s *OAuthTokenSync) SyncOauthTokenHook(ctx context.Context, id *authn.Ident
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if errors.Is(refreshErr, oauthtoken.ErrRetriesExhausted) {
|
||||||
|
ctxLogger.Warn("Retries have been exhausted for locking the DB for OAuth token refresh", "id", id.ID, "error", refreshErr)
|
||||||
|
return nil, refreshErr
|
||||||
|
}
|
||||||
|
|
||||||
ctxLogger.Error("Failed to refresh OAuth access token", "id", id.ID, "error", refreshErr)
|
ctxLogger.Error("Failed to refresh OAuth access token", "id", id.ID, "error", refreshErr)
|
||||||
|
|
||||||
// log the user out
|
// log the user out
|
||||||
|
@ -35,6 +35,7 @@ var (
|
|||||||
ErrNoRefreshTokenFound = errors.New("no refresh token found")
|
ErrNoRefreshTokenFound = errors.New("no refresh token found")
|
||||||
ErrNotAnOAuthProvider = errors.New("not an oauth provider")
|
ErrNotAnOAuthProvider = errors.New("not an oauth provider")
|
||||||
ErrCouldntRefreshToken = errors.New("could not refresh token")
|
ErrCouldntRefreshToken = errors.New("could not refresh token")
|
||||||
|
ErrRetriesExhausted = errors.New("retries exhausted")
|
||||||
)
|
)
|
||||||
|
|
||||||
type Service struct {
|
type Service struct {
|
||||||
@ -270,7 +271,7 @@ func (o *Service) TryTokenRefresh(ctx context.Context, usr identity.Requester, s
|
|||||||
if attempts < 5 {
|
if attempts < 5 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return ErrCouldntRefreshToken
|
return ErrRetriesExhausted
|
||||||
}
|
}
|
||||||
|
|
||||||
var newToken *oauth2.Token
|
var newToken *oauth2.Token
|
||||||
|
@ -62,7 +62,11 @@ func (f *FakeAuthInfoStore) DeleteAuthInfo(ctx context.Context, cmd *login.Delet
|
|||||||
return f.ExpectedError
|
return f.ExpectedError
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestService_TryTokenRefresh(t *testing.T) {
|
func TestIntegration_TryTokenRefresh(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("skipping integration test")
|
||||||
|
}
|
||||||
|
|
||||||
unexpiredToken := &oauth2.Token{
|
unexpiredToken := &oauth2.Token{
|
||||||
AccessToken: "testaccess",
|
AccessToken: "testaccess",
|
||||||
RefreshToken: "testrefresh",
|
RefreshToken: "testrefresh",
|
||||||
@ -86,6 +90,7 @@ func TestService_TryTokenRefresh(t *testing.T) {
|
|||||||
socialConnector *socialtest.MockSocialConnector
|
socialConnector *socialtest.MockSocialConnector
|
||||||
socialService *socialtest.FakeSocialService
|
socialService *socialtest.FakeSocialService
|
||||||
|
|
||||||
|
store db.DB
|
||||||
service *Service
|
service *Service
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,6 +256,30 @@ func TestService_TryTokenRefresh(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expectedToken: unexpiredTokenWithIDToken,
|
expectedToken: unexpiredTokenWithIDToken,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
desc: "should return ErrRetriesExhausted when lock cannot be acquired",
|
||||||
|
identity: &authn.Identity{ID: "1234", Type: claims.TypeUser, AuthenticatedBy: login.GenericOAuthModule},
|
||||||
|
setup: func(env *environment) {
|
||||||
|
env.socialService.ExpectedAuthInfoProvider = &social.OAuthInfo{
|
||||||
|
UseRefreshToken: true,
|
||||||
|
}
|
||||||
|
env.authInfoService.ExpectedUserAuth = &login.UserAuth{
|
||||||
|
AuthModule: login.GenericOAuthModule,
|
||||||
|
AuthId: "subject",
|
||||||
|
UserId: 1234,
|
||||||
|
OAuthAccessToken: unexpiredTokenWithIDToken.AccessToken,
|
||||||
|
OAuthRefreshToken: unexpiredTokenWithIDToken.RefreshToken,
|
||||||
|
OAuthExpiry: unexpiredTokenWithIDToken.Expiry,
|
||||||
|
OAuthTokenType: unexpiredTokenWithIDToken.TokenType,
|
||||||
|
OAuthIdToken: EXPIRED_ID_TOKEN,
|
||||||
|
}
|
||||||
|
_ = env.store.WithDbSession(context.Background(), func(sess *db.Session) error {
|
||||||
|
_, err := sess.Exec(`INSERT INTO server_lock (operation_uid, last_execution, version) VALUES (?, ?, ?)`, "oauth-refresh-token-1234", time.Now().Add(2*time.Second).Unix(), 0)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
},
|
||||||
|
expectedErr: ErrRetriesExhausted,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.desc, func(t *testing.T) {
|
t.Run(tt.desc, func(t *testing.T) {
|
||||||
@ -265,6 +294,7 @@ func TestService_TryTokenRefresh(t *testing.T) {
|
|||||||
socialService: &socialtest.FakeSocialService{
|
socialService: &socialtest.FakeSocialService{
|
||||||
ExpectedConnector: socialConnector,
|
ExpectedConnector: socialConnector,
|
||||||
},
|
},
|
||||||
|
store: store,
|
||||||
}
|
}
|
||||||
|
|
||||||
if tt.setup != nil {
|
if tt.setup != nil {
|
||||||
@ -308,7 +338,11 @@ func TestService_TryTokenRefresh(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestService_TryTokenRefresh_WithExternalSessions(t *testing.T) {
|
func TestIntegration_TryTokenRefresh_WithExternalSessions(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("skipping integration test")
|
||||||
|
}
|
||||||
|
|
||||||
unexpiredToken := &oauth2.Token{
|
unexpiredToken := &oauth2.Token{
|
||||||
AccessToken: "testaccess",
|
AccessToken: "testaccess",
|
||||||
RefreshToken: "testrefresh",
|
RefreshToken: "testrefresh",
|
||||||
@ -338,6 +372,7 @@ func TestService_TryTokenRefresh_WithExternalSessions(t *testing.T) {
|
|||||||
socialConnector *socialtest.MockSocialConnector
|
socialConnector *socialtest.MockSocialConnector
|
||||||
socialService *socialtest.FakeSocialService
|
socialService *socialtest.FakeSocialService
|
||||||
|
|
||||||
|
store db.DB
|
||||||
service *Service
|
service *Service
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -509,6 +544,21 @@ func TestService_TryTokenRefresh_WithExternalSessions(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expectedToken: unexpiredTokenWithIDToken,
|
expectedToken: unexpiredTokenWithIDToken,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
desc: "should return ErrRetriesExhausted when lock cannot be acquired",
|
||||||
|
identity: &authn.Identity{ID: "1234", Type: claims.TypeUser, AuthenticatedBy: login.GenericOAuthModule},
|
||||||
|
setup: func(env *environment) {
|
||||||
|
env.socialService.ExpectedAuthInfoProvider = &social.OAuthInfo{
|
||||||
|
UseRefreshToken: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = env.store.WithDbSession(context.Background(), func(sess *db.Session) error {
|
||||||
|
_, err := sess.Exec(`INSERT INTO server_lock (operation_uid, last_execution, version) VALUES (?, ?, ?)`, "oauth-refresh-token-1234-1", time.Now().Add(2*time.Second).Unix(), 0)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
},
|
||||||
|
expectedErr: ErrRetriesExhausted,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.desc, func(t *testing.T) {
|
t.Run(tt.desc, func(t *testing.T) {
|
||||||
@ -523,6 +573,7 @@ func TestService_TryTokenRefresh_WithExternalSessions(t *testing.T) {
|
|||||||
socialService: &socialtest.FakeSocialService{
|
socialService: &socialtest.FakeSocialService{
|
||||||
ExpectedConnector: socialConnector,
|
ExpectedConnector: socialConnector,
|
||||||
},
|
},
|
||||||
|
store: store,
|
||||||
}
|
}
|
||||||
|
|
||||||
if tt.setup != nil {
|
if tt.setup != nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user