web-dev-qa-db-fra.com

"Connexion requise" 401 Message non autorisé lors de l'appel de l'API Google Agenda avec l'aide d'un compte de service via OAuth 2.0

Tout d’abord, laissez-moi vous expliquer ce que j’essaie de faire, car c’est une question en deux parties.

Je construis un service JAX-RS qui s'authentifie en interne avec un compte Google via OAuth2, afin qu'il puisse accéder à un agenda Google et le manipuler. Ce service sera appelé par un site Web (Joomla), qui permettra aux utilisateurs qui se connectent au site d'enregistrer leur adresse e-mail avec les événements du calendrier afin de pouvoir recevoir des notifications par e-mail à proximité. Ces utilisateurs n'ont aucune connaissance du compte Google sous-jacent.

Donc, ma première question est la suivante: utiliser l'authentification de compte de service est-il la bonne chose? En regardant les documents Google, c'est le seul cas d'utilisation qui concerne l'authentification non humaine (donc l'authentification de serveur à serveur).

Le deuxième problème est que j'ai écrit du code pour le faire, mais je reçois une réponse 401/Unauthorized en renvoyant l'erreur suivante lorsque j'appelle la méthode execute au flux Calendar. Ce code a été retiré des échantillons de Google.

J'utilise v3-rev3-1.5.0-beta de l'API Google Agenda. J'ai activé l'API de calendrier dans le compte, puis créé et téléchargé une clé privée utilisée dans le code ci-dessous.

J'appelle le code ci-dessous dans Eclipse localement, pas à partir d'un serveur Web.

Le premier appel que je fais est donc de récupérer un objet d'identification (les identifiants sont obscurcis par ceux de X):

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();

L'étape suivante consiste ensuite à appeler l'API de calendrier, comme suit (aucun autre code appelé entre les deux):

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

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

L'appel à list (). Execute () provoque l'erreur suivante:

com.google.api.client.googleapis.json.GoogleJsonResponseException: 401 non autorisé { "code": 401, "les erreurs" : [ { "domaine": "global", "location": "Autorisation", "locationType": "en-tête", "message": "Connexion requise", "raison": "requis" }], "message": "Connexion requise" } à l'adresse com.google.api.client.googleapis.json.GoogleJsonResponseException.from (GoogleJsonResponseException.Java:159) à l'adresse com.google.api.client.googleapis.json.GoogleJsonResponseException.execute (GoogleJsonResponseException.Java:187) à l'adresse com.google.api.client.googleapis.services.GoogleClient.executeUnparsed (GoogleClient.Java:115) à l'adresse com.google.api.client.http.json.JsonHttpRequest.executeUnparsed (JsonHttpRequest.Java:112) à l'adresse com.google.api.services.calendar.Calendar $ CalendarList $ List.execute (Calendar.Java:510) at uk.org.reigatepriorybowmen.Service.registerEmailForAllCalendarEvents (Service.Java:55) à uk.org.reigatepriorybowmen.Service.main (Service.Java:74)

Le compte de service est configuré comme suit:

enter image description here

Alors, qu'est-ce que je fais mal?! J'ai passé des heures à chercher des solutions sur Google, donc c'est mon dernier espoir.

Merci d'avance pour votre aide.

Justin

18
Justin Phillips

Quant à la 401, il semble étrange avec l'API de Google. Je trouve que la première fois que je lance mon application depuis un moment, puis à des intervalles apparemment aléatoires, des heures bien espacées, je reçois toujours un 401. Au deuxième ou troisième tour, il se connecte réellement.

Le comportement est même pris en compte dans l'exemple de code de l'API de tâche de Google ici .

Notez ce code:

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);

}

Dans le propre exemple de code fourni par Google, il a été prévu pour la première fois de fonctionner 401. Il pourrait s'agir d'un problème de sécurité particulier qui doit être traité dans le code.

5

Je pense que vous devrez spécifier l'utilisateur que vous essayez d'usurper.

La manière dont vous utilisez actuellement ServiceAccount n'est valide que pour accéder aux données relatives à votre application ou à votre compte de service lui-même. Puisque vous souhaitez accéder aux données de Google Agenda, vous devez être un administrateur de domaine Google Apps disposant des privilèges pour accéder aux données de votre utilisateur de domaine. Indiquez-moi si je ne suis pas correct. Les données de Google Agenda appartiennent aux utilisateurs. Vous devez donc spécifier quel utilisateur vous voulez imiter.

Pour cela, il semble que vous deviez utiliser GoogleCredential.Builder # setServiceAccountUser (String)

S'il vous plaît essayez:

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();

En outre, une fois que vous avez créé une clé de compte de service dans votre projet API (à partir de la console API), vous devez ajouter ce projet à la liste des applications tierces autorisées dans le cPanel. Plus d'informations à ce sujet peuvent être trouvées ici . Le "numéro de client" à utiliser est celui lié à la clé du compte de service et ressemble à <APP_ID>-<OTHER_KEY>.apps.googleusercontent.com

1
Nicolas Garnier

Je suis un développeur d'applications mobiles Android. Je crée une application à l'aide de l'API Google Calendar (OAuth 2.0). J'ai aussi fait face à cette même erreur. Mais maintenant, j'ai résolu l'erreur. J'écris le scénario et la solution pour cela.
L'URL d'obtention de l'événement de calendrier pour le calendrier particulier (API d'application mobile):

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

Dans cette URL, nous devons ajouter le calendarId. Le format de l'URL est;

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

Je n’encodais pas calendarId avant d’ajouter à l’URL . J'ai encodé calendarId, puis la requête Http. Tout s'est bien passé alors.

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;

Maintenant, les événements de calendrier sont renvoyés dans le JSON.

1
Khushboo

Un tel comportement peut se produire si vous n'avez pas ajouté d'utilisateur api dans le panneau d'administration (dans le panneau Documents de votre cas je suppose). Compte de service Google API, après la création de la clé privée) . En analytique, il ressemble à ceci:

example in google analytics

1
Dawid D

Avez-vous demandé la permission pour les portées appropriées? Vous devriez toujours inclure https://www.googleapis.com/auth/userinfo.profile

0
Kees de Kooter

OK, donc j'ai joué avec cela et j'ai passé l'erreur 401, mais je frappe maintenant un 403. Cependant, je vais détailler le code que j'ai écrit qui répond au moins au problème 401.

Je n'ai modifié aucun des paramètres du compte de service, ni régénéré aucune des clés, etc., c'est la même chose.

Une partie du code a été modifiée et je suis passé à la version v3r20lv1.12.0-beta de l’API du calendrier.

Le code ci-dessous, tel que détaillé ci-dessus dans le PO, reste le même, c'est-à-dire:

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();

Le code pour obtenir un handle pour le calendrier a changé, mais cela a été forcé par un changement d'API:

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

À ce stade, je peux parcourir le calendrier et voir les événements en exécutant le code suivant (cela affiche un résumé de l'événement):

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

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

Mais si j'essaie de mettre à jour un événement, j'obtiens une erreur 403 Forbidden. Je vais créer un fil distinct pour cela et relier les deux.

Merci a tous pour votre aide!

EDIT: ici est le nouveau fil.

0
Justin Phillips