mirror of https://github.com/grafana/grafana
OAuth: Support Forward OAuth Identity for backend data source plugins (#27055)
Adds support for the Forward OAuth Identity feature in backend data source plugins. Earlier this feature has only been supported for non-backend data source plugins. Fixes #26023 Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com>pull/28520/head
parent
b6bb069e08
commit
b3a868169b
@ -0,0 +1,90 @@ |
||||
package oauthtoken |
||||
|
||||
import ( |
||||
"context" |
||||
|
||||
"github.com/grafana/grafana/pkg/bus" |
||||
"github.com/grafana/grafana/pkg/infra/log" |
||||
"github.com/grafana/grafana/pkg/login/social" |
||||
"github.com/grafana/grafana/pkg/models" |
||||
"golang.org/x/oauth2" |
||||
) |
||||
|
||||
var ( |
||||
logger = log.New("oauthtoken") |
||||
) |
||||
|
||||
// GetCurrentOAuthToken returns the OAuth token, if any, for the authenticated user. Will try to refresh the token if it has expired.
|
||||
func GetCurrentOAuthToken(ctx context.Context, user *models.SignedInUser) *oauth2.Token { |
||||
if user == nil { |
||||
// No user, therefore no token
|
||||
return nil |
||||
} |
||||
|
||||
authInfoQuery := &models.GetAuthInfoQuery{UserId: user.UserId} |
||||
if err := bus.Dispatch(authInfoQuery); err != nil { |
||||
if err == models.ErrUserNotFound { |
||||
// Not necessarily an error. User may be logged in another way.
|
||||
logger.Debug("no OAuth token for user found", "userId", user.UserId, "username", user.Login) |
||||
} else { |
||||
logger.Error("failed to get OAuth token for user", "userId", user.UserId, "username", user.Login, "error", err) |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
authProvider := authInfoQuery.Result.AuthModule |
||||
connect, err := social.GetConnector(authProvider) |
||||
if err != nil { |
||||
logger.Error("failed to get OAuth connector", "provider", authProvider, "error", err) |
||||
return nil |
||||
} |
||||
|
||||
client, err := social.GetOAuthHttpClient(authProvider) |
||||
if err != nil { |
||||
logger.Error("failed to get OAuth http client", "provider", authProvider, "error", err) |
||||
return nil |
||||
} |
||||
ctx = context.WithValue(ctx, oauth2.HTTPClient, client) |
||||
|
||||
persistedToken := &oauth2.Token{ |
||||
AccessToken: authInfoQuery.Result.OAuthAccessToken, |
||||
Expiry: authInfoQuery.Result.OAuthExpiry, |
||||
RefreshToken: authInfoQuery.Result.OAuthRefreshToken, |
||||
TokenType: authInfoQuery.Result.OAuthTokenType, |
||||
} |
||||
// TokenSource handles refreshing the token if it has expired
|
||||
token, err := connect.TokenSource(ctx, persistedToken).Token() |
||||
if err != nil { |
||||
logger.Error("failed to retrieve OAuth access token", "provider", authInfoQuery.Result.AuthModule, "userId", user.UserId, "username", user.Login, "error", err) |
||||
return nil |
||||
} |
||||
|
||||
// If the tokens are not the same, update the entry in the DB
|
||||
if !tokensEq(persistedToken, token) { |
||||
updateAuthCommand := &models.UpdateAuthInfoCommand{ |
||||
UserId: authInfoQuery.Result.UserId, |
||||
AuthModule: authInfoQuery.Result.AuthModule, |
||||
AuthId: authInfoQuery.Result.AuthId, |
||||
OAuthToken: token, |
||||
} |
||||
if err := bus.Dispatch(updateAuthCommand); err != nil { |
||||
logger.Error("failed to update auth info during token refresh", "userId", user.UserId, "username", user.Login, "error", err) |
||||
return nil |
||||
} |
||||
logger.Debug("updated OAuth info for user", "userId", user.UserId, "username", user.Login) |
||||
} |
||||
return token |
||||
} |
||||
|
||||
// IsOAuthPassThruEnabled returns true if Forward OAuth Identity (oauthPassThru) is enabled for the provided data source.
|
||||
func IsOAuthPassThruEnabled(ds *models.DataSource) bool { |
||||
return ds.JsonData != nil && ds.JsonData.Get("oauthPassThru").MustBool() |
||||
} |
||||
|
||||
// tokensEq checks for OAuth2 token equivalence given the fields of the struct Grafana is interested in
|
||||
func tokensEq(t1, t2 *oauth2.Token) bool { |
||||
return t1.AccessToken == t2.AccessToken && |
||||
t1.RefreshToken == t2.RefreshToken && |
||||
t1.Expiry == t2.Expiry && |
||||
t1.TokenType == t2.TokenType |
||||
} |
Loading…
Reference in new issue