サービスアカウントでGoogleカレンダーイベントを作成中にエラーが発生しました

Dec 19 2020

カレンダーにGoogleカレンダーイベントを作成し、そのイベントの参加者として他のユーザーを追加する必要があります。目的は、ユーザーの同意を得ずにカレンダーイベントをアプリケーションユーザーに送信することです(O-Auth)。

Googleのドキュメントを読んだ後、サービスアカウントが必要であることがわかりました。そこで、G-Suiteのメールアドレスの1つである[email protected]からプロジェクトとサービスアカウントを作成し、同じカレンダーAPIを有効にしました。

内容が次のキーペア(JSON)を作成してダウンロードしました。

{
  "type": "service_account",
  "project_id": "*****",
  "private_key_id": "bdbbcd**************49f77d599f2",
  "private_key": "**"
  "client_email": "******@*****.iam.gserviceaccount.com",
  "client_id": "11083******576856",
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://oauth2.googleapis.com/token",
  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
  "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/****dev%40*****kdev.iam.gserviceaccount.com"
}

そして、ドキュメントに従って、私は認証フローコードを書くことに進みました、

public static GoogleCredential doOauth( String credsPath ) throws IOException
    {
        GoogleCredential credential = GoogleCredential.fromStream(new FileInputStream(credsPath))
                .createScoped(Collections.singleton(CalendarScopes.CALENDAR));
        System.out.println(credential);
        return credential;
    }

credentialオブジェクトがキーファイルから細部のほとんどを持っています。しかし、フィールド、serviceAccountUseraccessTokenrefreshTokenclientAuthenticationrequestInitializer持っているnull価値を。(私はここで何か間違っていると推測しています)

ここで、を使用credentialObjectして、イベントを作成するためのドキュメントに従ってコードを書き続けました。

GoogleCredential credential = doOauth(CREDENTIALS_FILE_PATH);
        Event event = new Event().setSummary("Google I/O 2015").setLocation("800 Howard St., San Francisco, CA 94103")
                .setDescription("A chance to hear more about Google's developer products.");
        final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();

        DateTime startDateTime = new DateTime("2020-12-28T09:00:00-07:00");
        EventDateTime start = new EventDateTime().setDateTime(startDateTime).setTimeZone("America/Los_Angeles");
        event.setStart(start);

        DateTime endDateTime = new DateTime("2020-12-28T17:00:00-07:00");
        EventDateTime end = new EventDateTime().setDateTime(endDateTime).setTimeZone("America/Los_Angeles");
        event.setEnd(end);

        String[] recurrence = new String[] { "RRULE:FREQ=DAILY;COUNT=2" };
        event.setRecurrence(Arrays.asList(recurrence));

        EventAttendee[] attendees = new EventAttendee[] { new EventAttendee().setEmail("[email protected]") };
        event.setAttendees(Arrays.asList(attendees));

        EventReminder[] reminderOverrides = new EventReminder[] {
                new EventReminder().setMethod("email").setMinutes(24 * 60),
                new EventReminder().setMethod("popup").setMinutes(10), };
        Event.Reminders reminders = new Event.Reminders().setUseDefault(false)
                .setOverrides(Arrays.asList(reminderOverrides));
        event.setReminders(reminders);
        String calendarId = "primary";
        Calendar service = new Calendar.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential)
                .setApplicationName("testapp").build();
        event = service.events().insert(calendarId, event).execute();
        System.out.printf("Event created: %s\n", event.getHtmlLink());

しかし、これはエラーになりました、

{
  "code" : 403,
  "errors" : [ {
    "domain" : "calendar",
    "message" : "Service accounts cannot invite attendees without Domain-Wide Delegation of Authority.",
    "reason" : "forbiddenForServiceAccounts"
  } ],
  "message" : "Service accounts cannot invite attendees without Domain-Wide Delegation of Authority."
}

に時間を費やした後Domain-Wide Delegation、g-suiteから他のユーザーとしてイベントを送信する必要がある場合にこれが必要であることがわかりました。これは私の問題には必要ありません。しかし、デバッグするために、私は先に進みDomain-Wide Delegation、プログラムを提供して再実行しました。同じエラーが再び発生しました。

そこで、eventオブジェクトから招待者/参加者を削除して、アプリケーションを再実行しました。今回はプログラムはエラーなしで実行されましたが、クリックするとイベントリンクが生成されましたCould not find the requested event

グーグル開発者リンクのJavaクライアントライブラリを介してサービスアカウントを使用する例は見当たりません。

ここで何が問題になっているのか、プロジェクトからGoogleカレンダーイベントを正確に作成する方法に関する公式/作業ドキュメントに、他の(gスイート以外の)ユーザーを参加者として追加して、私がいないようにしてください。他のユーザーから自分のカレンダーにイベントを追加することに同意を得るには?

ありがとうございました。

回答

DaImTo Dec 21 2020 at 15:06

最初に言いたいのですが、これは何か新しいことです。多くの質問が表示されている場合や、サービスアカウントが招待状を送信できたため、これを行う必要があると記載されていないチュートリアルでは、これはGoogleがロックダウンしたものです。約1年前。

これは機能するはずですが、Gsuiteアカウントにアクセスできなくなったため、テストしていません。gsuite管理者に別のユーザーへのドメイン全体の委任を設定させる必要があります。次に、サービスアカウントはそのユーザーになりすます必要があります。これにより、そのユーザーが招待状を送信しているように見えます。

それがどのように追加されるかについて私が持っている唯一の例は.netにあります

.netの例

var gsuiteUser = "[email protected]";
var serviceAccountCredentialInitializer = new ServiceAccountCredential.Initializer(serviceAccount)
            {
                User = gsuiteUser,
                Scopes = new[] { GmailService.Scope.GmailSend, GmailService.Scope.GmailLabels }

            }.FromCertificate(certificate);

Javaの例の推測

私はJava開発者ではありませんが、.netクライアントライブラリとJavaクライアントライブラリは、開発方法が非常に似ています。setServiceAccountUserというメソッドを探していると思います

GoogleCredential credential = new  GoogleCredential.Builder().setTransport(HTTP_TRANSPORT)
                .setJsonFactory(JSON_FACTORY)
                .setServiceAccountId(SERVICE_ACCOUNT_EMAIL)
                .setServiceAccountScopes(CalendarScopes.CALENDAR)
                .setServiceAccountPrivateKeyFromP12File(credsPath))
                .setServiceAccountUser(gsuiteUser)
                .build();
  • サービスアカウントのOAuth2