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

C# 使用OAuth 2和服务帐户访问较旧的GDataAPI(电子表格API),c#,.net,oauth-2.0,gdata-api,google-spreadsheet-api,C#,.net,Oauth 2.0,Gdata Api,Google Spreadsheet Api,简短的问题是,这是否可能,如果可能,如何实现 轮廓 我有一个.NET应用程序,它目前使用一个服务帐户使用Google Drive API访问Google应用程序域中的信息。这可以很好地使用和代码,这是目前我正在做的一个非常好的基本示例 我现在想做的是扩展它,通过“新的”google api dotnet客户端库,它使用旧的“GData”库,正如通过 更老的,特别是电子表格API(可能还有更多) 问题 这就是困难所在。前一个图书馆做的正是我想要的,正如上面第一段中的第二个链接所证明的那样——事实上

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

轮廓 我有一个.NET应用程序,它目前使用一个服务帐户使用Google Drive API访问Google应用程序域中的信息。这可以很好地使用和代码,这是目前我正在做的一个非常好的基本示例

我现在想做的是扩展它,通过“新的”google api dotnet客户端库,它使用旧的“GData”库,正如通过 更老的,特别是电子表格API(可能还有更多)

问题 这就是困难所在。前一个图书馆做的正是我想要的,正如上面第一段中的第二个链接所证明的那样——事实上我自己也在做然而。。。尽管第二个库已经更新,除了OAuth 1.0和其他较旧的身份验证技术外,还支持OAuth 2.0,但它不允许我需要的“代表所有用户的服务帐户”操作(从大量的谷歌搜索和跟踪错误中可以看出)

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

理想溶液 理想情况下,我希望通过某种方式让
Google.GData.Spreadsheets.Spreadsheets服务
实例能够利用我正在使用的
Google.api.Authentication.Auth2Authenticator
实例。。。以某种方式这样的巫术可能吗?我错过了显而易见的事情

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

帮忙

其他想法 我已经考虑过——并且暂时拒绝——从零开始,编写自己的库来实现这一目标。这有两个原因:

  • gdata库已经存在,并且由许多可能比我更聪明的人开发。我不是那么自大,我相信我能做得更好
  • 我不确定这些旧API是否支持/允许使用OAuth2 with service account方法
  • 我一直希望避免的另一种方法是,根据这里的答案,可能不得不退回到使用两条腿的OAuth 1.0来完成部分内容。我不希望这样,因为应用程序的某些部分依赖于一个旧的auth方法,而其他部分则使用新的方法,这让我感觉不对。还有更多的错误


    更新 我已经考虑了将GDataRequestFactory和GDataRequest子类化的可能性,这样我就可以创建自己的请求工厂,并以
    Google.api.Authentication.Auth2Authenticator
    为例(当然,还是
    Google.api.Authentication.iaauthenticator
    的一个实例)它可以在调用请求之前对请求进行身份验证。然而。。。GDataRequest的构造函数是内部的,这已经阻止了我


    看起来这并不是我的本意。

    我通过子类化GDataRequestFactory并创建我自己的由GDataRequest实现的接口实现来解决这个问题。此实现封装了通过反射实例化的GDataRequest实例,并添加了使用IAAuthenticator实例(在我的示例中为Auth2Authenticator)执行身份验证所需的代码

    我写了一篇关于它的博客文章,并添加了一个示例作为要点:


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

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

    首先,从“新API”开始(使用
    Google.API.Auth
    nuget包),通过设置一个
    ServiceAccountCredential
    以下Google的:

    //在旧的api中,它访问了主要api帐户的工作表,不再访问了
    //**重要信息**通过邀请“serviceAccountEmail”地址与服务帐户共享电子表格
    字符串serviceAccountEmail=“12345697-abcdefghijklmnop@developer.gserviceaccount.com";
    var证书=新的X509Certificate2(@“key.p12”,“notasecret”,X509keystrageFlags.Exportable);
    ServiceAccountCredential credential=新ServiceAccountCredential(
    新ServiceAccountCredential.初始值设定项(serviceAccountEmail)
    {
    作用域=新[]{”https://spreadsheets.google.com/feeds", "https://docs.google.com/feeds" }
    }.FromCertificate(证书));
    
    告诉凭证请求访问令牌:

    credential.RequestAccessTokenAsync(System.Threading.CancellationToken.None).Wait();
    
    现在是时候切换回“旧API”领域了(使用
    Google.GData.Spreadsheets
    nuget包)。首先构建
    电子表格服务
    (与谷歌类似):

    spreadsheets服务=新的电子表格服务(“MySpreadsheetIntegration-v1”);
    
    要使用服务帐户身份验证,我们将创建
    GDataRequestFactory
    的一个实例,并设置一个自定义
    Authorization
    头:

    var requestFactory=new-GDataRequestFactory(“我的应用程序用户代理”);
    Add(string.Format(“Authorization:Bearer{0}”,credential.Token.AccessToken));
    
    最后,将
    电子表格服务
    请求工厂
    属性设置为此新工厂:

    service.RequestFactory=RequestFactory;
    
    然后继续使用
    电子表格服务
    ,就像使用任何其他技术进行身份验证一样。(提示:通过邀请
    服务帐户em与服务帐户共享电子表格
    
    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;
        }
    }