ここのサンプル コードに従いました: https://code.google.com/p/google-api-dotnet-client/wiki/OAuth2#Service_Accounts
認証が次のエラーで失敗する: DotNetOpenAuth.Messaging.ProtocolException: ダイレクト メッセージの送信中または応答の取得中にエラーが発生しました。
内部例外は System.Net.WebException です。リモート サーバーがエラーを返しました: (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 は、サービス アカウントの作成に使用される Google 開発者アカウントの API コンソールで有効になっています。このアカウントは Google Apps ドメインの一部ではありません。
また、AssertionFlowClient の ServiceAccountUser プロパティを指定してこれをテストしました。私は今、これが必要であると信じています - 手動で作成された JWT を使用した CalendarService のテストに成功しました (以下の OAuth トークンの手動作成を参照)。 prn 属性が含まれていないトークンを作成しようとすると、404 エラーを受け取りました要求 (つまり、ServiceAccountUser は含まれません)。
Google Apps ドメインの設定
Google Apps ドメインで、このサービス アカウントにカレンダーへのアクセスを許可しました。
クライアント名: [中略].apps.googleusercontent.com
API スコープ:
- カレンダー (読み取り/書き込み) https://www.google.com/calendar/feeds/
- カレンダー (読み書き可能) https://www.googleapis.com/auth/calendar
インストールされた NuGet パッケージ
- Google.Apis (1.5.0-ベータ)
- Google.Apis.Calendar.v3 (1.5.0.59-ベータ版)
- Google.Apis.Authentication (1.5.0-ベータ)
リクエストとレスポンス
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 ドメインのユーザーのカレンダーからイベントを正常に取得できます。ご参考までに、カレンダーへのアクセスはサービス アカウントで可能です。
Authorization ヘッダーを追加するだけの IAuthenticator 実装を次に示します。
public class Authenticator : IAuthenticator
{
public void ApplyAuthenticationToRequest(System.Net.HttpWebRequest request)
{
request.Headers.Add(HttpRequestHeader.Authorization, "Bearer <token here>");
}
}