使用OAuth 2和服务帐户访问旧的GData API(电子表格API)

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

大纲

我有一个.NET应用程序,它目前使用服务帐户使用Google Drive API访问Google Apps域中的信息。 这可以正常使用google-api-dotnet-client库和代码沿着这里的示例所示的相同行 – 这是目前我正在做的非常好的基本示例。

我现在要做的是扩展它,以及使用 “新”google-api-dotnet-client库提供的那些API ,它使用旧的“GData”库,通过较旧的google-gdata库提供 ,特别是Spreadsheets API(也许还有更多)。

问题

这就是出现困难的地方。 前一个库正是我想要的,正如上面第一段中的第二个链接所certificate的那样 – 以及我自己做它的事实。 但是 ……虽然第二个库已经更新以支持OAuth 2.0以及其他旧的auth技术,但它没有 – 据我所知,从广泛的谷歌搜索和追踪错误 – 允许“代表我所有用户的服务帐户“我需要的操作。

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

理想的解决方案

理想情况下,我会喜欢某种方式让Google.GData.Spreadsheets.SpreadsheetsService实例能够利用我已经在某种程度上使用的Google.Apis.Authentication.Auth2Authenticator实例。 这种巫术有可能吗? 我错过了那个显而易见的东西吗?

如果不这样做,我很高兴再次完成整个OAuth2“断言流客户端”的舞蹈,如果我不得不以旧版图书馆可以处理的方式。

救命?

其他想法

我考虑过 – 暂时拒绝 – 选择从头开始编写我自己的库来实现这一目标。 这有两个原因:

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

我一直希望避免但可能不得不依赖于这里的答案的另一种方法是使用双腿OAuth 1.0来实现这一点。 我不愿意,因为应用程序的某些部分依赖于一种旧的auth方法,而其他部分则使用这种新方式对我来说感觉不对。 还有更多的问题……


更新

我已经考虑过inheritanceGDataRequestFactory和GDataRequest的可能性,所以我可以创建自己的请求工厂,并使用Google.Apis.Authentication.Auth2Authenticator的实例(好吧,无论如何, Google.Apis.Authentication.IAuthenticator的实例)它可以在调用之前介入validation请求。 但是…… GDataRequest的构造函数是内部的,这已经阻止了我。

它真的看起来像是不应该的。

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

首先,在Google的服务帐户示例之后,通过设置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 (类似于Google的示例 ):

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

要使用服务帐户身份validation,我们将创建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就像使用任何其他技术进行身份validation一样。 ( 提示:通过邀请serviceAccountEmail地址到工作表来与服务帐户共享电子表格)

我设法通过inheritanceGDataRequestFactory并创建我自己的GDataRequest实现的接口实现来解决这个问题。 此实现包含通过reflection实例化的GDataRequest实例,并添加必要的代码以使用IAuthenticator的实例(在我的示例中为Auth2Authenticator)执行身份validation。

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

  • 博客:使用.NET,OAuth 2.0和服务帐户使用Google的电子表格API
  • 要点4244834

如果它可以帮助你(BSD许可证),请随意使用它。

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

有没有人想过将credentials-object中的参数直接写入OAuth2Parameters-Object?

我这样做了,效果很好:

 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; } }