Mon p’tit coin du web
Accueil du site > Code > Authentification OAuth pour Google Data

Authentification OAuth pour Google Data

samedi 11 décembre 2010, par Nicolas

Problématique

Pour me simplifier la vie, je souhaitais automatiser la génération de statistiques Google Analytics. Cependant il est bien sûr hors de question de mettre mon login et mot de passe dans les sources, ou même en paramètres.

J’ai choisi de faire mon code en Java, avec le Java Client Library qui fournit de nombreuses classes et fonctions pour interagir avec les services Google.

En fouillant sur le net, il y a une documentation importante sur l’authentification via OAuth, par exemple ce document. Sans oublier l’exemple Java associé, qui donne de nombreuses informations.

Mécanisme

Sans réexpliquer toute la chaîne OAuth, ce mécanisme permet à une application d’accéder à des données d’un utilisateur sans connaître le login ou mot de passe associé, en ayant ses propres tokens d’authentification qu’il est possible de révoquer à tout moment.

Première étape : obtenir un token

Le but est de générer une URL sur laquelle l’utilisateur pourra se connecter, et autoriser l’application à accéder aux données.

Cette étape est très largement décrite dans, et inspirée de, l’exemple Java OAuth, cependant cet exemple fait toute la boucle d’un seul coup (y compris la désactivation du token) et ne montre pas comment conserver les tokens pour les réutiliser plus tard. La version qui suit est donc tronquée avant révocation, et adaptée pour afficher en sortie toutes les informations nécessaires.

Google mentionne dans leur documentation l’existence d’un compte anonymous, qui est celui utilisé ici [1].

  1. /* Copyright (c) 2008 Google Inc.
  2.  * Modified by N. Weeger, 2010.
  3.  *
  4.  * Licensed under the Apache License, Version 2.0 (the "License");
  5.  * you may not use this file except in compliance with the License.
  6.  * You may obtain a copy of the License at
  7.  *
  8.  *     http://www.apache.org/licenses/LICENSE-2.0
  9.  *
  10.  * Unless required by applicable law or agreed to in writing, software
  11.  * distributed under the License is distributed on an "AS IS" BASIS,
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13.  * See the License for the specific language governing permissions and
  14.  * limitations under the License.
  15.  */
  16.  
  17. package oauthtest;
  18.  
  19. import com.google.gdata.client.GoogleService;
  20. import com.google.gdata.client.authn.oauth.GoogleOAuthHelper;
  21. import com.google.gdata.client.authn.oauth.GoogleOAuthParameters;
  22. import com.google.gdata.client.authn.oauth.OAuthException;
  23. import com.google.gdata.client.authn.oauth.OAuthHmacSha1Signer;
  24. import com.google.gdata.client.authn.oauth.OAuthRsaSha1Signer;
  25. import com.google.gdata.client.authn.oauth.OAuthSigner;
  26. import com.google.gdata.data.BaseEntry;
  27. import com.google.gdata.data.BaseFeed;
  28. import com.google.gdata.data.Feed;
  29.  
  30. import java.net.URL;
  31. import java.util.logging.Level;
  32. import java.util.logging.Logger;
  33.  
  34. public class Main {
  35.  
  36.     /**
  37.      * @param args the command line arguments
  38.      */
  39.     public static void main(String[] args) {
  40.     try {
  41.       ////////////////////////////////////////////////////
  42.       // STEP 1: Gather the user's information
  43.       ////////////////////////////////////////////////////
  44.       // This step collects information from the user, such as the consumer key
  45.       // and which service to query.  This is just a general setup routine, and
  46.       // the method by which you collect user information may be different in your
  47.       // implementation.
  48.       // (tronqué ici, pas nécessaire)
  49.  
  50.       ////////////////////////////////////////////////////
  51.       // STEP 2: Set up the OAuth objects
  52.       ////////////////////////////////////////////////////
  53.       // You first need to initialize a few OAuth-related objects.
  54.       // GoogleOAuthParameters holds all the parameters related to OAuth.
  55.       // OAuthSigner is responsible for signing the OAuth base string.
  56.       GoogleOAuthParameters oauthParameters = new GoogleOAuthParameters();
  57.       // Set your OAuth Consumer Key (which you can register at
  58.       // https://www.google.com/accounts/ManageDomains).
  59.       oauthParameters.setOAuthConsumerKey("anonymous");
  60.       // Initialize the OAuth Signer.  If you are using RSA-SHA1, you must provide
  61.       // your private key as a Base-64 string conforming to the PKCS #8 standard.
  62.       // Visit http://code.google.com/apis/gdata/authsub.html#Registered to learn
  63.       // more about creating a key/certificate pair.  If you are using HMAC-SHA1,
  64.       // you must set your OAuth Consumer Secret, which can be obtained at
  65.       // https://www.google.com/accounts/ManageDomains.
  66.       OAuthSigner signer;
  67.       oauthParameters.setOAuthConsumerSecret("anonymous");
  68.       signer = new OAuthHmacSha1Signer();
  69.       // Finally create a new GoogleOAuthHelperObject.  This is the object you
  70.       // will use for all OAuth-related interaction.
  71.       GoogleOAuthHelper oauthHelper = new GoogleOAuthHelper(signer);
  72.  
  73.       ////////////////////////////////////////////////////
  74.       // STEP 3: Get the Authorization URL
  75.       ////////////////////////////////////////////////////
  76.       // Set the scope for this particular service.
  77.       oauthParameters.setScope("https://www.google.com/analytics/feeds/data");
  78.       // This method also makes a request to get the unauthorized request token,
  79.       // and adds it to the oauthParameters object, along with the token secret
  80.       // (if it is present).
  81.       oauthHelper.getUnauthorizedRequestToken(oauthParameters);
  82.       // Get the authorization url.  The user of your application must visit
  83.       // this url in order to authorize with Google.  If you are building a
  84.       // browser-based application, you can redirect the user to the authorization
  85.       // url.
  86.       String requestUrl = oauthHelper.createUserAuthorizationUrl(oauthParameters);
  87.       System.out.println(requestUrl);
  88.       System.out.println("Please visit the URL above to authorize your OAuth " + "request token.  Once that is complete, press any key to " + "continue...");
  89.       System.in.read();
  90.  
  91.       ////////////////////////////////////////////////////
  92.       // STEP 4: Get the Access Token
  93.       ////////////////////////////////////////////////////
  94.       // Once the user authorizes with Google, the request token can be exchanged
  95.       // for a long-lived access token.  If you are building a browser-based
  96.       // application, you should parse the incoming request token from the url and
  97.       // set it in GoogleOAuthParameters before calling getAccessToken().
  98.       String token = oauthHelper.getAccessToken(oauthParameters);
  99.       System.out.println("OAuth Access Token: " + token);
  100.       System.out.println("OAuth Token Secret: " + oauthParameters.getOAuthTokenSecret());
  101.       System.out.println();
  102.     } catch (Exception ex) {
  103.       Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
  104.       System.out.print(ex);
  105.     }
  106.     }
  107. }

Ce code lancé en console affichera une URL à aller ouvrir dans son navigateur préféré afin d’autoriser l’application (« anonymous ») à accéder aux données, ici Google Analytics.

Une fois l’autorisation accordée, faire continuer l’application (appuyer sur la touche « Entrée ») et deux tokens seront affichées, le « OAuth Access Token » et le « OAuth Token Secret » [2], qu’il faut recopier dans un coin.

Cette méthode est manuelle, mais comme elle n’est à faire qu’une fois pour une application, cela ne me pose pas de souci.

Deuxième étape : accéder aux données

Une fois qu’on a les tokens (à ne pas révoquer sous peine de devoir recommencer), l’accès au service est assez simple.

  1.       AnalyticsService service = new AnalyticsService("ApplicationDeTest");
  2.       OAuthParameters params = new GoogleOAuthParameters();
  3.       OAuthSigner signer;
  4.  
  5.       // Encore une fois on met ce compte anonyme
  6.       params.setOAuthConsumerSecret("anonymous");
  7.       params.setOAuthConsumerKey("anonymous");
  8.  
  9.       // C'est ici qu'il faut mettre les valeurs récupérées à l'étape précédente
  10.       params.setOAuthToken("OAuth Access Token");
  11.       params.setOAuthTokenSecret("OAuth Token Secret");
  12.  
  13.       signer = new OAuthHmacSha1Signer();
  14.       service.setOAuthCredentials(params, signer);
  15.  
  16.       DataQuery query = new DataQuery(new URL("https://www.google.com/analytics/feeds/data"));
  17.       // Renseigner ici vos paramètres pour le Query
  18.       DataFeed dataFeed = service.getFeed(query.getUrl(), DataFeed.class);
  19.       // Et voilà, on a les données, plus qu'à les exploiter

Conclusion

Une fois tous ces éléments agencés, l’application finale peut accéder aux données Analytics et être déployée sans trop de risques de sécurité sur des serveurs. Au hasard elle peut être réutilisée via Hudson pour régulièrement générer des statistiques.

Un prochain axe de recherche que je regarderai peut-être est de comprendre comment obtenir un vrai compte d’application, plutôt que d’utiliser le « anonymous ».

Notes

[1] Il y a peut-être des contraintes sur le nombre d’applications pouvant être enregistrées sur un compte avec ce login, mais pour une démonstration cela suffira.

[2] La documentation Google mentionne en passant qu’il faut effectivement stocker ce « token token secret » dans le cas d’un encodage HMAC-SHA1, mais n’explique pas plus que ça, et n’en reparle jamais semble-t-il. Ne pas avoir remarqué cette ligne dans la documentation m’a coûté plusieurs heures de tests... Ceci dit, un spécialiste OAuth aurait sans doute compris cela plus rapidement.

Répondre à cet article

SPIP | squelette | | Plan du site | Suivre la vie du site RSS 2.0