web-dev-qa-db-ja.com

「ログインが必要です」401 OAuth 2.0を介してサービスアカウントを使用してv3 Google Calendar APIを呼び出すときの不正なメッセージ

まず、これは2つの部分からなる質問なので、私が何をしようとしているのかを説明しましょう。

私は、OAuth2を介してGoogleアカウントで内部認証するJAX-RSサービスを構築しています。これにより、Googleカレンダーにアクセスして操作できます。このサービスはWebサイト(Joomla)によって呼び出され、サイトにログインしたユーザーは自分のメールアドレスをカレンダーイベントに登録できるため、イベントが近づいたときにメール通知を受け取ることができます。これらのユーザーは、基盤となるGoogleアカウントを認識していません。

最初の質問ですが、サービスアカウント認証を使用するのは適切ですか。 Googleドキュメントを見ると、人間以外の認証(サーバー間認証)に対応できる唯一のユースケースです。

2番目の問題は、これを実行するためのコードをいくつか作成したことですが、カレンダーフィードの実行メソッドを呼び出すと、401/Unauthorizedレスポンスが返され、次のエラーが返されます。このコードは、Googleのサンプルから削除されました。

Google Calendar APIのv3-rev3-1.5.0-betaを使用しています。アカウントでCalendar APIをオンにし、以下のコードで使用される秘密鍵を作成してダウンロードしました。

私はEclipseで以下のコードをWebサーバーからではなくローカルに呼び出しています。

したがって、私が最初に呼び出すのは、資格情報オブジェクト(Xで難読化されたID)を取得することです。

private static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
private static final JsonFactory JSON_FACTORY = new JacksonFactory();
.
.
.
GoogleCredential credential = new GoogleCredential.Builder().setTransport(HTTP_TRANSPORT)
            .setJsonFactory(JSON_FACTORY)
            .setServiceAccountId("[email protected]")
            .setServiceAccountScopes(CalendarScopes.CALENDAR)
            .setServiceAccountPrivateKeyFromP12File(new File("D:/3cd8XXXXXXXXXXXXXXXXXXXXXXXXXXXXXd635e-privatekey.p12"))
            .build();

次のステップは、カレンダーAPIを呼び出すことです(その間に他のコードは呼び出されません)。

 Calendar calendar = Calendar.builder(HTTP_TRANSPORT, JSON_FACTORY)
        .setApplicationName("TestApp-Calendar/1.0").setHttpRequestInitializer(credential)
        .build();

 CalendarList feed = calendar.calendarList().list().execute();

List()。execute()を呼び出すと、次のエラーが発生します。

com.google.api.client.googleapis.json.GoogleJsonResponseException:401 Unauthorized {"code":401、 "errors":[{"domain": "global"、 "location": "Authorization"、 "locationType": " header "、" message ":" Login Required "、" reason ":" required "}]、" message ":" Login Required "} at com.google.api.client.googleapis.json.GoogleJsonResponseException.from(GoogleJsonResponseException。 Java:159)com.google.api.client.googleapis.json.GoogleJsonResponseException.execute(GoogleJsonResponseException.Java:187)at com.google.api.client.googleapis.services.GoogleClient.executeUnparsed(GoogleClient.Java:115) com.google.api.client.http.json.JsonHttpRequest.executeUnparsed(JsonHttpRequest.Java:112)at com.google.api.services.calendar.Calendar $ CalendarList $ List.execute(Calendar.Java:510)at uk uk.org.reigatepriorybowmen.Service.main(Service.Java:74)の.org.reigatepriorybowmen.Service.registerEmailForAllCalendarEvents(Service.Java:55)

サービスアカウントは次のように設定されます。

enter image description here

だから、私は何が間違っているのですか?私は解決策を探すために何時間もグーグルで過ごしてきたので、これが私の最後の希望です。

よろしくお願いします。

ジャスティン

18
Justin Phillips

401に関しては、GoogleのAPIとは少し変わっているようです。しばらくして初めてア​​プリを実行したとき、一見ランダムな間隔で数時間離れて、常に401を取得します。2回目または3回目では、実際にログインします。

この動作は、GoogleのTask APIサンプルコード here でも説明されています。

このコードに注意してください:

void onRequestCompleted() {
received401 = false;
}

void handleGoogleException(IOException e) {
if (e instanceof GoogleJsonResponseException) {
  GoogleJsonResponseException exception = (GoogleJsonResponseException) e;
  if (exception.getStatusCode() == 401 && !received401) {
    received401 = true;
    accountManager.invalidateAuthToken(credential.getAccessToken());
    credential.setAccessToken(null);
    SharedPreferences.Editor editor2 = settings.edit();
    editor2.remove(PREF_AUTH_TOKEN);
    editor2.commit();
    gotAccount();
    return;
  }
}
Log.e(TAG, e.getMessage(), e);

}

Google独自のコードサンプルでは、​​401が初めてプロビジョニングを行いました。コードで処理する必要がある特別なセキュリティ問題の可能性があります。

なりすまそうとしているユーザーを指定する必要があると思います。

現在ServiceAccountを使用している方法は、アプリケーションまたはサービスアカウント自体に関連するデータにアクセスする場合にのみ有効です。 Googleカレンダーのデータにアクセスしたいので、ドメインユーザーのデータにアクセスする権限を持つGoogle Appsドメイン管理者である必要があります。正しくない場合はお知らせください。 Googleカレンダーのデータはユーザーのものなので、なりすまそうとしているユーザーを指定する必要があります。

Javaを使用してこれを行うには、 GoogleCredential.Builder#setServiceAccountUser(String) を使用する必要があるようです

してみてください:

GoogleCredential credential = new GoogleCredential.Builder().setTransport(HTTP_TRANSPORT)
        .setJsonFactory(JSON_FACTORY)
        .setServiceAccountId("[email protected]")
        .setServiceAccountScopes(CalendarScopes.CALENDAR)
        .setServiceAccountPrivateKeyFromP12File(new File("/path/to/privatekey.p12"))
        .setServiceAccountUser("[email protected]")
        .build();

また、APIコンソールで(APIコンソールから)サービスアカウントキーを作成したら、このプロジェクトをcPanelの承認済みサードパーティアプリのリストに追加する必要があります。この詳細については、 ここ を参照してください。使用する必要がある「クライアントID」は、サービスアカウントキーにバインドされたもので、<APP_ID>-<OTHER_KEY>.apps.googleusercontent.comのようになります。

1
Nicolas Garnier

私はAndroidモバイルアプリデベロッパーです。GoogleCalendar API(OAuth 2.0)を使用してアプリケーションを作成しています。これも同じエラーに直面しました。しかし、今、エラーを解決しました。シナリオとその解決策。
特定のカレンダーのカレンダーイベントを取得するためのURL(モバイルアプリAPI):

https://www.googleapis.com/calendar/v3/calendars/en.indian%23holiday%40group.v.calendar.google.com/events?access_token=ya29.AHES6ZQyP3iDZM8PF-7ZqZE8oKmWAgUX55sPGPTjGbM5A

このURLには、calendarIdを追加する必要があります。 URLの形式は次のとおりです。

https://www.googleapis.com/calendar/v3/calendars/<calendarId>/events

URLに追加する前にcalendarIdをエンコードしていませんでした。 calendarIdをエンコードしてから、Httpリクエストを作成しました。その後、すべてがうまくいきました。

final String accessToken=dataStore.getAccessToken();

    List<NameValuePair> params = new LinkedList<NameValuePair>();
    params.add(new BasicNameValuePair("access_token", accessToken));
    String paramString = URLEncodedUtils.format(params, "utf-8");

    final String url = String.format(AuthConstants.CALENDAR_EVENTS_URI,URLEncoder.encode(calendarId))+"?"+ paramString;

これで、カレンダーイベントがJSONで返されます。

1
Khushboo

このような現象は、管理者パネル(おそらくドキュメントパネル)でapiユーザーを追加しなかった場合に発生する可能性があります。秘密鍵を作成した後のGoogle APIサービスアカウント)。分析では、次のようになります。

example in google analytics

1
Dawid D

適切なスコープの許可を要求しましたか?常にhttps://www.googleapis.com/auth/userinfo.profile

0
Kees de Kooter

OK、それで私はこれで遊んでいて401エラーを乗り越えましたが、今は403に達しています。しかし、少なくとも401の問題に答えるように書いたコードについて詳しく説明します。

サービスアカウントの設定を変更したり、キーなどを再生成したりはしていません。すべて同じです。

変更点はコードの一部であり、カレンダーAPIのv3r20lv1.12.0-betaにアップグレードしました。

以下のコードは、上記のOPで詳しく説明されているように、同じままです。

private static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
private static final JsonFactory JSON_FACTORY = new JacksonFactory();
.
.
.
GoogleCredential credential = new GoogleCredential.Builder().setTransport(HTTP_TRANSPORT)
        .setJsonFactory(JSON_FACTORY)
        .setServiceAccountId("[email protected]")
        .setServiceAccountScopes(CalendarScopes.CALENDAR)
        .setServiceAccountPrivateKeyFromP12File(new File("D:/3cd8XXXXXXXXXXXXXXXXXXXXXXXXXXXXXd635e-privatekey.p12"))
        .build();

カレンダーのハンドルを取得するコードは変更されましたが、これはAPIの変更によって強制されました。

Calendar calendar = new Calendar(HTTP_TRANSPORT, JSON_FACTORY, credential);

この時点で、次のコードを実行することで、カレンダーを反復処理してイベントを確認できます(これにより、イベントの概要が出力されます)。

Events events = calendar.events().list(CALENDAR_ID).execute();

for (Event event : events.getItems())
{
    System.out.println(event.getSummary());
}

しかし、イベントを更新しようとすると、403 Forbiddenエラーが発生します。このために別のスレッドを生成し、2つのスレッドをリンクします。

あなたの助けをありがとう!

編集: ここ は新しいスレッドです。

0
Justin Phillips