[release-12.2.1] Auth: Fix render user OAuth passthrough (#112092)

Auth: Fix render user OAuth passthrough (#111636)

* devenv: fix volumes section when sources don't contain one

* wip

* Working correctly with improvedExternalSessionHandling on

* Remove not needed lines

* Working with the old flow, tests

* Handle compatibility with the feature toggle, tests wip

* Tests

* Cleanup

* Address feedback

* Align tests

* Add comment

* Fix issue with session removal after the invalidation of tokens

* Remove commented out code

* clean up

(cherry picked from commit 53f4803e98)

Co-authored-by: Misi <mgyongyosi@users.noreply.github.com>
This commit is contained in:
grafana-delivery-bot[bot]
2025-10-09 15:47:18 +02:00
committed by GitHub
co-authored by Misi
parent b3551d6d0d
commit 05e3befe99
12 changed files with 1911 additions and 322 deletions
@@ -93,7 +93,11 @@ func (s *OAuthTokenSync) SyncOauthTokenHook(ctx context.Context, id *authn.Ident
updateCtx, cancel := context.WithTimeout(context.WithoutCancel(ctx), 15*time.Second)
defer cancel()
token, refreshErr := s.service.TryTokenRefresh(updateCtx, id, id.SessionToken)
token, refreshErr := s.service.TryTokenRefresh(updateCtx, id, &oauthtoken.TokenRefreshMetadata{
ExternalSessionID: id.SessionToken.ExternalSessionId,
AuthModule: id.GetAuthenticatedBy(),
AuthID: id.GetAuthID(),
})
if refreshErr != nil {
if errors.Is(refreshErr, context.Canceled) {
return nil, nil
@@ -107,7 +111,7 @@ func (s *OAuthTokenSync) SyncOauthTokenHook(ctx context.Context, id *authn.Ident
ctxLogger.Error("Failed to refresh OAuth access token", "id", id.ID, "error", refreshErr)
// log the user out
if err := s.sessionService.RevokeToken(ctx, id.SessionToken, false); err != nil {
if err := s.sessionService.RevokeToken(ctx, id.SessionToken, false); err != nil && !errors.Is(err, auth.ErrUserTokenNotFound) {
ctxLogger.Warn("Failed to revoke session token", "id", id.ID, "tokenId", id.SessionToken.Id, "error", err)
}
@@ -25,6 +25,7 @@ import (
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/login"
"github.com/grafana/grafana/pkg/services/oauthtoken"
"github.com/grafana/grafana/pkg/services/oauthtoken/oauthtokentest"
)
@@ -77,6 +78,14 @@ func TestOAuthTokenSync_SyncOAuthTokenHook(t *testing.T) {
expectRevokeTokenCalled: false,
expectToken: &login.UserAuth{OAuthExpiry: time.Now().Add(10 * time.Minute)},
},
{
desc: "should not invalidate session if token refresh fails with no refresh token",
identity: &authn.Identity{ID: "1", Type: claims.TypeUser, SessionToken: &auth.UserToken{}, AuthenticatedBy: login.AzureADAuthModule},
expectedTryRefreshErr: oauthtoken.ErrNoRefreshTokenFound,
expectTryRefreshTokenCalled: true,
expectRevokeTokenCalled: true,
expectedErr: oauthtoken.ErrNoRefreshTokenFound,
},
// TODO: address coverage of oauthtoken sync
}
@@ -89,7 +98,7 @@ func TestOAuthTokenSync_SyncOAuthTokenHook(t *testing.T) {
)
service := &oauthtokentest.MockOauthTokenService{
TryTokenRefreshFunc: func(ctx context.Context, usr identity.Requester, _ *auth.UserToken) (*oauth2.Token, error) {
TryTokenRefreshFunc: func(ctx context.Context, usr identity.Requester, _ *oauthtoken.TokenRefreshMetadata) (*oauth2.Token, error) {
tryRefreshCalled = true
return nil, tt.expectedTryRefreshErr
},
+3 -1
View File
@@ -297,7 +297,9 @@ func (c *OAuth) Logout(ctx context.Context, user identity.Requester, sessionToke
ctxLogger := c.log.FromContext(ctx).New("userID", userID)
if err := c.oauthService.InvalidateOAuthTokens(ctx, user, sessionToken); err != nil {
if err := c.oauthService.InvalidateOAuthTokens(ctx, user, &oauthtoken.TokenRefreshMetadata{
ExternalSessionID: sessionToken.ExternalSessionId,
AuthModule: user.GetAuthenticatedBy()}); err != nil {
ctxLogger.Error("Failed to invalidate tokens", "error", err)
}
+4 -2
View File
@@ -19,10 +19,12 @@ import (
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/login/social"
"github.com/grafana/grafana/pkg/login/social/socialtest"
"github.com/grafana/grafana/pkg/models/usertoken"
"github.com/grafana/grafana/pkg/services/auth"
"github.com/grafana/grafana/pkg/services/authn"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/login"
"github.com/grafana/grafana/pkg/services/oauthtoken"
"github.com/grafana/grafana/pkg/services/oauthtoken/oauthtokentest"
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/setting"
@@ -481,7 +483,7 @@ func TestOAuth_Logout(t *testing.T) {
"id_token": "some.id.token",
})
},
InvalidateOAuthTokensFunc: func(_ context.Context, _ identity.Requester, _ *auth.UserToken) error {
InvalidateOAuthTokensFunc: func(_ context.Context, _ identity.Requester, _ *oauthtoken.TokenRefreshMetadata) error {
invalidateTokenCalled = true
return nil
},
@@ -492,7 +494,7 @@ func TestOAuth_Logout(t *testing.T) {
}
c := ProvideOAuth(authn.ClientWithPrefix("azuread"), tt.cfg, mockService, fakeSocialSvc, &setting.OSSImpl{Cfg: tt.cfg}, featuremgmt.WithFeatures(), tracing.InitializeTracerForTest())
redirect, ok := c.Logout(context.Background(), &authn.Identity{ID: "1", Type: claims.TypeUser}, nil)
redirect, ok := c.Logout(context.Background(), &authn.Identity{ID: "1", Type: claims.TypeUser}, &usertoken.UserToken{})
assert.Equal(t, tt.expectedOK, ok)
if tt.expectedOK {