【问题标题】:How do I authenticate with the Google v3 api using a Service Account如何使用服务帐户向 Google v3 api 进行身份验证
【发布时间】:2013-09-23 00:57:38
【问题描述】:

我在这里遵循了示例代码: https://code.google.com/p/google-api-dotnet-client/wiki/OAuth2#Service_Accounts

授权失败: DotNetOpenAuth.Messaging.ProtocolException: 发送直接消息或获取响应时出错。

内部异常是 System.Net.WebException: The remote server returned an error: (400) Bad Request。响应正文为空,响应 URI 为 https://accounts.google.com/o/oauth2/token

在下面的响应中你会看到具体的错误是 invalid_grant。

这是我的代码:

var certificate = new X509Certificate2(CertificatePath, "notasecret", X509KeyStorageFlags.Exportable);
var provider = new AssertionFlowClient(GoogleAuthenticationServer.Description, certificate)
    {
        ServiceAccountId = "<...>@developer.gserviceaccount.com",
        Scope = CalendarService.Scopes.Calendar.GetStringValue()
    };

var authenticator = new OAuth2Authenticator<AssertionFlowClient>(provider, AssertionFlowClient.GetState);

var calendarService =
    new CalendarService(new BaseClientService.Initializer()
        {
            Authenticator = authenticator
        });

var eventList = calendarService.Events.List("<id of the calendar>").Execute();

证书和 ServiceAccountId 正确。我进行了三次检查,并且已经重新生成了证书。 Google 日历 API 在 API 控制台中为用于创建服务帐户的 google 开发人员帐户打开。此帐户不属于 Google Apps 域。

我还使用指定的 AssertionFlowClient 的 ServiceAccountUser 属性对此进行了测试。我现在认为这是必需的 - 在我使用手动创建的 JWT 成功测试 CalendarService 时(请参阅下面的 OAuth 令牌的手动创建工作),当 prn 属性不包含在尝试创建令牌时,我收到 404 错误声明(即不包括 ServiceAccountUser)。

Google Apps 域配置

在 Google Apps 域中,我已授予此服务帐户访问日历的权限。

客户名称:[snip].apps.googleusercontent.com

API 范围:

已安装的 NuGet 包

  • Google.Apis (1.5.0-beta)
  • Google.Apis.Calendar.v3(1.5.0.59-beta)
  • Google.Apis.Authentication(1.5.0-beta)

请求和响应

POST https://accounts.google.com/o/oauth2/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded; charset=utf-8
User-Agent: DotNetOpenAuth/4.0.0.11165
Host: accounts.google.com
Cache-Control: no-store,no-cache
Pragma: no-cache
Content-Length: 606
Connection: Keep-Alive

grant_type=assertion&assertion_type=http%3A%2F%2Foauth.net%2Fgrant_type%2Fjwt%2F1.0%2Fbearer&assertion=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI5NzUzOTk3NzMyNi01NHFvMXY4OW5iZTk4dGNlbGIycWY0cDdjNThzYjhmMkBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsInNjb3BlIjoiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vYXV0aC9jYWxlbmRhciIsImF1ZCI6Imh0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbS9vL29hdXRoMi90b2tlbiIsImV4cCI6MTM3OTU5MTA4MywiaWF0IjoxMzc5NTg3NDgzfQ.Ls_sv40MfB8MAD92JFcFiW5YYoRytQ3e2PA8RV_hn4FJfVHDo6uCSunN7950H2boO6LfX9EMrpjaf8ZyNyHyrQucQaWwfIFD6F2FpnqlcNkzXoqWMCwkt-k-8ypGMSZfFCEkhw8QOrlIPFZb6qx61689n08G9tZMTzHGYc2b8Gk

仔细检查,断言似乎是正确的,在这里解码:

{"alg":"RS256","typ":"JWT"}{"iss":"97539977326-54qo1v89nbe98tcelb2qf4p7c58sb8f2@developer.gserviceaccount.com","scope":"https://www.googleapis.com/auth/calendar","aud":"https://accounts.google.com/o/oauth2/token","exp":1379591083,"iat":1379587483}

HTTP/1.1 400 Bad Request
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: Fri, 01 Jan 1990 00:00:00 GMT
Date: Thu, 19 Sep 2013 10:44:42 GMT
Content-Type: application/json
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
Server: GSE
Alternate-Protocol: 443:quic
Content-Length: 31

{
  "error" : "invalid_grant"
}

手动创建 OAuth 令牌作品

为了确认我的设置正确,我使用 google-oauth-jwt 手动创建了一个令牌(此处为:https://github.com/extrabacon/google-oauth-jwt)。我能够使用上面代码中使用的相同属性成功创建令牌。一旦我创建了令牌并在自定义 IAuthenticator 中使用它,我就可以成功地从目标 Google Apps 域中的用户日历中检索事件。因此,如果您想知道,可以使用服务帐户访问日历!

这是 IAuthenticator 实现,它只是添加了 Authorization 标头:

public class Authenticator : IAuthenticator
{
  public void ApplyAuthenticationToRequest(System.Net.HttpWebRequest request)
  {
    request.Headers.Add(HttpRequestHeader.Authorization, "Bearer <token here>");
  }
}

【问题讨论】:

  • 400 通常意味着您的 HTTP 请求中有语法错误。我建议您实际查看来回的 HTTP 请求/响应。如果您没有发现问题所在,请在此处发布。
  • 谢谢蒂姆。我已经添加了上面的请求和响应。我继续看这个,希望 invalid_grant 错误能对此有所了解。

标签: .net google-calendar-api google-oauth google-api-dotnet-client jwt


【解决方案1】:

我不确定自从提出这个问题后是否发生了变化,但 Google 日历确实支持服务帐户。

设置用于 Google Calendar API 的服务帐户时,您只需使用服务帐户电子邮件地址。访问 Google 日历网站。找到日历设置,然后转到日历选项卡,找到您要访问的日历,然后单击“共享:编辑设置”添加服务帐户电子邮件地址,就像您添加个人电子邮件地址一样。这将为服务帐户提供与您与任何其他用户共享该服务帐户相同的访问权限。

string[] scopes = new string[] {
    CalendarService.Scope.Calendar, // Manage your calendars
    CalendarService.Scope.CalendarReadonly // View your Calendars
 };

 var certificate = new X509Certificate2(keyFilePath, "notasecret", X509KeyStorageFlags.Exportable);

ServiceAccountCredential credential = new ServiceAccountCredential(
    new ServiceAccountCredential.Initializer(serviceAccountEmail) {
        Scopes = scopes
    }.FromCertificate(certificate));

// Create the service.
    CalendarService service = new CalendarService(new BaseClientService.Initializer() {
        HttpClientInitializer = credential,
        ApplicationName = "Calendar API Sample",
    });

Google Calendar API Authentication C# 窃取的代码

【讨论】:

    【解决方案2】:

    据我所知,日历服务不支持服务帐户。

    您可以检查 G+ 示例 (https://code.google.com/p/google-api-dotnet-client/source/browse/Plus.ServiceAccount/Program.cs?repo=samples) 是否与您尝试在此处为日历 API 运行的代码相同。 Google plus 支持服务帐户,所以它应该可以工作。

    为什么不能运行 OAuth2 用户流而不是服务帐户?

    更新: 毕竟,日历 API 支持服务帐户。很抱歉造成混乱。

    【讨论】:

    • 我们正在为使用 Google Apps 的公司构建一个集成,我们希望管理员为所有用户设置访问权限。管理员授权访问域的用户流程可以工作,但看起来不可能。我没有在任何类型的文档中找到说明日历范围是否支持服务帐户的官方消息。但是,我看到许多帖子表明用户确实可以使用此功能。 SO上的一个例子:stackoverflow.com/questions/18457241/…
    • 此外,该示例似乎并未真正获得 oAuth 令牌。如果在创建服务时不指定 IAuthenticator 实例,它将成功运行。当我指定 IAuthenticator 实例时,不会调用获取令牌(我已使用 Fiddler 验证)。
    • 我已确认可以使用服务帐户访问日历。请参阅上面我的答案的更新。
    • 您是否尝试使用相同的代码运行我提到的 plus 示例?它奏效了吗?我不知道日历支持服务帐户。顺便说一句,在我们的下一个版本中,我们将摆脱 DotNetOpenAuth 并添加我们自己的 OAuth2、ServiceAccount、JWT 等实现。
    • 正如我上面提到的,该示例实际上并没有获得 OAuth 令牌。即使我没有设置 IAuthenticator 实例,它也确实有效。
    【解决方案3】:

    我建议使用the answer 中的DaImTo 提供的ServiceAccountCredential,但您可以通过编程方式指定用户的电子邮件。您不必进入他们的谷歌日历设置:

    string serviceAccountEmail = "mail@project.iam.gserviceaccount.com";
    
    // Read certificate from PKCS#12 file downloaded from the Google Developer Console
    var certificate = new X509Certificate2(@"files/projectKey.p12", "notasecret",
        X509KeyStorageFlags.Exportable);
    
    // Create Service Account Credential to add to specific user's calendar
    ServiceAccountCredential credential = new ServiceAccountCredential(
        new ServiceAccountCredential.Initializer(serviceAccountEmail)
        {
            User = userEmail,
            Scopes = new[] { CalendarService.Scope.Calendar } // Manage user's calendars
        }.FromCertificate(certificate));
    }
    
    // Initialize service using credential
    CalendarService service = new CalendarService(new BaseClientService.Initializer()
    {
          HttpClientInitializer = credential,
          ApplicationName = "project"
    });
    

    一旦我在 Google Developers Console 中为我的项目创建了服务凭据并启用了 Google 日历 API,这对我的应用程序有效——我能够在用户的日历中创建/读取/更新/删除事件。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-11-25
      • 1970-01-01
      • 2017-11-03
      • 2020-05-21
      • 2019-08-25
      • 2015-05-12
      • 1970-01-01
      相关资源
      最近更新 更多