How to save data within CheckHealth

Hey there!
I’m writing a backend plugin for Grafana and I am unsure on how to proceed an issue.

I’m currently writing a datasource plugin that requires authorizing with Google OAuth2. It goes like this:

  • a person goes to a Google page, asking for consent for a specific client ID and scopes
  • a person is redirected to some URI
  • this code (one-time use) is sent to a Google server, then access and refresh tokens are returned
  • these tokens are used to request Google API.

I’ve figured out how to get a code from user: make a link to Google on a frontend side with redirect_uri as the Grafana editing datasource URI. This will redirect user back, then I can get the code from the query string.

Question is, I need to exchange it and receive access and refresh token and somehow store it in the database. Can I do it from within a healthcheck endpoint?

Here’s currently what I have:

// CheckHealth handles health checks sent from Grafana to the plugin.
// The main use case for these health checks is the test button on the
// datasource configuration page which allows users to verify that
// a datasource is working as expected.
func (td *SampleDatasource) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) {
	log.DefaultLogger.Info("QueryData", "request", req.PluginContext.DataSourceInstanceSettings)

	if val, ok := req.PluginContext.DataSourceInstanceSettings.DecryptedSecureJSONData["code"]; !ok || val == "" {
		return &backend.CheckHealthResult{
			Status:  backend.HealthStatusError,
			Message: "Code is not provided. Please press \"Sign in with Google\"",
		}, nil
	}

	if val, ok := req.PluginContext.DataSourceInstanceSettings.DecryptedSecureJSONData["clientSecret"]; !ok || val == "" {
		return &backend.CheckHealthResult{
			Status:  backend.HealthStatusError,
			Message: "Client secret is not provided",
		}, nil
	}

	jsonData, err := simplejson.NewJson([]byte(req.PluginContext.DataSourceInstanceSettings.JSONData))
	if err != nil {
		return &backend.CheckHealthResult{
			Status:  backend.HealthStatusError,
			Message: "Could not parse JSON",
		}, nil
	}

	clientID := jsonData.Get("clientId").MustString()
	if clientID == "" {
		return &backend.CheckHealthResult{
			Status:  backend.HealthStatusError,
			Message: "Client ID is not provided",
		}, nil
	}

	redirectURI := jsonData.Get("redirectURI").MustString()
	if redirectURI == "" {
		return &backend.CheckHealthResult{
			Status:  backend.HealthStatusError,
			Message: "Redirect URI is not provided",
		}, nil
	}

	clientSecret := req.PluginContext.DataSourceInstanceSettings.DecryptedSecureJSONData["clientSecret"]
	code := req.PluginContext.DataSourceInstanceSettings.DecryptedSecureJSONData["code"]

	log.DefaultLogger.Info("QueryData", "clientId", clientID)
	log.DefaultLogger.Info("QueryData", "clientSecret", clientSecret)
	log.DefaultLogger.Info("QueryData", "code", code)
	log.DefaultLogger.Info("QueryData", "redirectURI", redirectURI)

	if token, err = getToken(ctx, clientID, clientSecret, code, redirectURI); err != nil {
		return &backend.CheckHealthResult{
			Status:  backend.HealthStatusError,
			Message: "Error authorizing",
		}, nil
	}
	

	return &backend.CheckHealthResult{
		Status:  backend.HealthStatusOk,
		Message: "Everything is okay.",
	}, nil
}

func getToken(ctx context.Context, clientID string, clientSecret string, code string, redirectURI string) (*oauth2.Token, error) {
	conf := &oauth2.Config{
		ClientID:     clientID,
		ClientSecret: clientSecret,
		Endpoint:     google.Endpoint,
		RedirectURL:  redirectURI,
	}

	token, err := conf.Exchange(ctx, code)
	log.DefaultLogger.Debug("QueryData", "err", err)
	log.DefaultLogger.Debug("QueryData", "token", token)
	if err != nil {
		return nil, err
	}

	return token, err
}

I need to store access and refresh tokens somehow on backend based on what user has sent and I’m not sure how to proceed here. How can I do this?
The code is saved to the DB but unfortunately it only can be use once.

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.