我在这里遵循了示例代码: 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>");
}
}