【问题标题】:Accessing older GData APIs (Spreadsheet API) using OAuth 2 and a service account使用 OAuth 2 和服务帐户访问较旧的 GData API(电子表格 API)
【发布时间】:2012-11-19 19:48:56
【问题描述】:

简短的问题是这是否可能,如果可能,如何实现?

大纲

我有一个 .NET 应用程序,该应用程序当前使用服务帐户通过 Google Drive API 访问 Google Apps 域中的信息。这可以很好地使用google-api-dotnet-client library 和代码沿相同的行as shown in the samples here - 这是目前我正在做的一个非常好的基本示例。

我现在要做的是通过“新”google-api-dotnet-client 库扩展它以及 using those APIs provided,它使用较旧的“GData”库,如通过 较旧的 google-gdata library,特别是电子表格 API(可能还会有更多)。

问题

这就是困难所在。正如上面第一段中的第二个链接所证明的那样,以前的图书馆完全符合我的要求 - 以及我自己做的事实。 然而...虽然第二个库已更新为支持 OAuth 2.0 以及 OAuth 1.0 和其他较旧的身份验证技术,但据我从广泛的谷歌搜索和跟踪中得知,它并不支持and-error - 允许我需要的“代表我所有用户的服务帐户”操作。

我的问题是我是否遗漏了一些东西(可能是难以找到或没有记录的东西),这可以让我做我想做的事。如果做不到这一点,有什么办法可以强制这种行为并使这两个库并行运行?

理想的解决方案

理想情况下,我希望通过某种方式让Google.GData.Spreadsheets.SpreadsheetsService 实例能够利用我已经在使用的Google.Apis.Authentication.Auth2Authenticator<AssertionFlowClient> 实例......不知何故。这样的巫术是可能的吗?我错过了显而易见的事情吗?

如果做不到这一点,我很乐意在必要时以旧库可以处理的某种方式再次完成整个 OAuth2“断言流客户端”。

帮助?

其他想法

我已经考虑过——但暂时拒绝了——从头开始编写自己的库来实现这一点的选择。这有两个原因:

  1. gdata 库已经存在,并且由许多可能比我聪明的人开发。我没有那么自大,我相信我可以做得更好。
  2. 我不确定这些旧 API 是否支持/允许使用服务帐户的 OAuth2 方法。

我一直希望避免但可能不得不回退的另一种方法,具体取决于这里的答案,将使用 2-legged OAuth 1.0 作为其中的一部分。我不希望这样做,因为应用程序的某些部分依赖于一种旧的身份验证方法,而其他部分则以一种不错的新方法来做,这对我来说是错误的。还有更多的问题......


更新

我已经考虑了将 GDataRequestFactory 和 GDataRequest 子类化的可能性,因此我可以创建自己的请求工厂,并使用 Google.Apis.Authentication.Auth2Authenticator<AssertionFlowClient> 的实例(好吧,无论如何是 Google.Apis.Authentication.IAuthenticator 的实例),它可以介入验证请求就在它被调用之前。但是...... GDataRequest 的构造函数是内部的,这让我停止了。

看起来真的不是这样。

【问题讨论】:

标签: c# .net oauth-2.0 gdata-api google-spreadsheet-api


【解决方案1】:

我设法通过继承 GDataRequestFactory 并创建我自己的 GDataRequest 实现的接口实现来解决这个问题。此实现包装了一个通过反射实例化的 GDataRequest 实例,并添加了必要的代码以使用 IAuthenticator 实例(在我的例子中为 Auth2Authenticator)执行身份验证。

我在上面写了一篇博文并添加了一个示例作为要点:

如果对您有帮助,请随意使用(BSD 许可证)。

【讨论】:

  • 那是一些优秀的代码。不幸的是,从那时起,Google 将 IAuthenticator 标记为过时并使用 ServiceAccountCredential。它似乎是与以前完全不同的机制,所以我无法使用您的方法制作适配器。希望他们能在某个时候发布与电子表格兼容的 API!
  • IAuthenticator现在不可用?
【解决方案2】:

为了其他人遇到这个问题(现在在接受的答案中链接到的解决方案使用了不推荐使用的代码),这是我解决它的方法:

首先,通过在 Google 的 Service Account example 之后设置 ServiceAccountCredential,从“新 API”领域开始(使用 Google.Apis.Auth nuget 包):

//In the old api, this accessed the main api accounts' sheets, not anymore
//** Important ** share spreadsheets with the Service Account by inviting the "serviceAccountEmail" address to the sheet
string serviceAccountEmail = "12345697-abcdefghijklmnop@developer.gserviceaccount.com";

var certificate = new X509Certificate2(@"key.p12", "notasecret", X509KeyStorageFlags.Exportable);

ServiceAccountCredential credential = new ServiceAccountCredential(
   new ServiceAccountCredential.Initializer(serviceAccountEmail)
   {
       Scopes = new[] { "https://spreadsheets.google.com/feeds", "https://docs.google.com/feeds" }
   }.FromCertificate(certificate));

告诉凭据请求访问令牌:

credential.RequestAccessTokenAsync(System.Threading.CancellationToken.None).Wait();

现在是时候切换回“旧 API”领域了(使用 Google.GData.Spreadsheets nuget 包)。首先构造SpreadsheetsService(类似于谷歌的example):

SpreadsheetsService service = new SpreadsheetsService("MySpreadsheetIntegration-v1");

要使用服务帐户身份验证,我们将创建 GDataRequestFactory 的实例并设置自定义 Authorization 标头:

var requestFactory = new GDataRequestFactory("My App User Agent");
requestFactory.CustomHeaders.Add(string.Format("Authorization: Bearer {0}", credential.Token.AccessToken));

最后,将SpreadsheetsServiceRequestFactory 属性设置为这个新工厂:

service.RequestFactory = requestFactory;

继续使用SpreadsheetsService,就像使用任何其他技术进行身份验证一样。 (提示:通过邀请serviceAccountEmail 地址到工作表与服务帐户共享电子表格)

【讨论】:

  • 已更新已接受的答案,因为它现在是完成此任务的最佳(唯一)方法,并且在使用新库时与我原来的解决方案一样有效。值得注意的是,需要为所使用的服务适当地设置范围:-)。
  • 感谢您的回答,帮助我指明了正确的方向。对于其他调整此方法以访问 EmailSettings API 的人,您需要使用管理员主电子邮件地址向 .Initializer 添加一个“用户”属性。
【解决方案3】:

嘿,偶然发现了同样的问题并产生了不同的解决方案:

有没有人考虑将参数从凭证对象直接写入 OAuth2Parameters 对象?

我这样做了,效果很好:

public class OAuthTest
{  
    OAuth2Parameters param = new OAuth2Parameters();

    public OAuthTest()
    {
        Debug.WriteLine("Calling: AuthGoogleDataInterface()");
        bool init = AuthGoogleDataInterface();
        if (init)
        {
            GOAuth2RequestFactory requestFactory = new GOAuth2RequestFactory(null, "My App User Agent", this.param);
            //requestFactory.CustomHeaders.Add(string.Format("Authorization: Bearer {0}", credential.Token.AccessToken));
            var service = new SpreadsheetsService("MyService");
            service.RequestFactory = requestFactory;
            SpreadsheetQuery query = new SpreadsheetQuery();

            // Make a request to the API and get all spreadsheets.
            SpreadsheetFeed feed = service.Query(query);

            // Iterate through all of the spreadsheets returned
            foreach (SpreadsheetEntry entry in feed.Entries)
            {
                // Print the title of this spreadsheet to the screen
                Debug.WriteLine(entry.Title.Text);
            }
        }
        Debug.WriteLine(m_Init);
    }

    private bool AuthGoogleDataInterface()
    {
        bool b_success;
        try
        {
            Console.WriteLine("New User Credential");
            // New User Credential
            UserCredential credential;
            using (var stream = new FileStream("client_secrets.json", FileMode.Open, FileAccess.Read))
            {
                GoogleClientSecrets GCSecrets = GoogleClientSecrets.Load(stream);
                string[] ArrScope = new[] { "https://spreadsheets.google.com/feeds", "https://docs.google.com/feeds" };
                credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
                    GCSecrets.Secrets,
                    ArrScope,
                    "user", CancellationToken.None,
                new FileDataStore("My.cal")).Result;
                // put the Information generated for the credentials object into the OAuth2Parameters-Object to access the Spreadsheets
                this.param.ClientId = GCSecrets.Secrets.ClientId; //CLIENT_ID;
                this.param.ClientSecret = GCSecrets.Secrets.ClientSecret; //CLIENT_SECRET;
                this.param.RedirectUri = "urn:ietf:wg:oauth:2.0:oob"; //REDIRECT_URI;
                this.param.Scope = ArrScope.ToString();
                this.param.AccessToken = credential.Token.AccessToken;
                this.param.RefreshToken = credential.Token.RefreshToken;
            }

            Debug.WriteLine("AuthGoogleDataInterface: Success");
            b_success = true;
        }
        catch (Exception e)
        {
            Debug.WriteLine(e.ToString());
            b_success = false;
        }
        return b_success;
    }
}

【讨论】:

  • 我怎样才能得到 client_secrets.json 文件?
  • 我记得你是直接从你的谷歌开发者应用程序中得到的。
  • @abatishchev:真的吗?
  • 我尝试时至少应该设置一个客户端密码(已安装或 Web)。我为 Installed 创建 JSON 文件。
猜你喜欢
  • 2012-07-27
  • 2020-11-01
  • 1970-01-01
  • 2022-01-15
  • 2012-11-24
  • 1970-01-01
  • 1970-01-01
  • 2014-02-01
  • 2018-10-06
相关资源
最近更新 更多