首页 文章

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

提问于
浏览
9

我在这里按照示例代码: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 Domain Configuration

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

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

API范围:

Installed NuGet packages

  • Google.Apis(1.5.0-beta)

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

  • Google.Apis.Authentication(1.5.0-beta)

Request and Response

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

Manual Creation of OAuth Token Works

为了确认我已正确设置,我使用google-oauth-jwt手动创建了一个令牌(此处为:https://github.com/extrabacon/google-oauth-jwt) . 我能够使用目标Google Apps域中的相同属性成功创建令牌 . 所以,万一你想知道, Calendar access is possible with Service Accounts!

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

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

3 回答

  • 0

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

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

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

    您可以使用与此处针对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支持服务帐户 . 对困惑感到抱歉 .

  • 1

    我建议使用the answer中提供的ServiceAccountCredential,但是您可以指定用户's e-mail programatically. You don' t进入他们的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,我就可以在我的应用程序中工作 - 我能够在用户的日历中创建/读取/更新/删除事件 .

相关问题