如何使用服务帐户对Google v3 api进行身份验证

时间:2013-09-18 10:04:21

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

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

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

内部异常是System.Net.WebException:远程服务器返回错误:(400)错误请求。响应正文为空,响应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控制台中启用了Google Calendar API。此帐户不属于Google Apps域。

我还使用指定的AssertionFlowClient的ServiceAccountUser属性对此进行了测试。我现在认为这是必需的 - 在我使用手动创建的JWT成功测试CalendarService时(请参阅下面的手动创建OAuth令牌工程),当prn属性未包含在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>");
  }
}

3 个答案:

答案 0 :(得分:2)

我不确定自问这个问题后是否有变化,但谷歌日历确实支持服务帐户。

设置用于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#

答案 1 :(得分:1)

我知道日历服务不支持服务帐户。

您可以使用与此处针对Calendar API运行的相同代码来检查G +示例是否有效(https://code.google.com/p/google-api-dotnet-client/source/browse/Plus.ServiceAccount/Program.cs?repo=samples)。 Google plus支持服务帐户,因此它应该有效。

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

更新: 毕竟,Calendar API支持服务帐户。对不起,感到困惑。

答案 2 :(得分:0)

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

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,我就可以在我的应用程序中使用这个功能 - 我能够在用户中创建/读取/更新/删除事件&#39;日历。