Google Sign-In pour les applications côté serveur

Pour utiliser les services Google pour le compte d'un utilisateur lorsqu'il est hors connexion, vous devez utiliser une procédure hybride côté serveur. Dans ce cas, l'utilisateur autorise votre application côté client à l'aide du client API JavaScript et envoyez un code d'autorisation unique spécial à votre serveur. Votre serveur échange ce code à usage unique pour acquérir ses propres jetons d'accès et d'actualisation auprès de Google. Le serveur peut alors effectuer ses propres appels d'API, ce qui peut être effectué lorsque l'utilisateur est hors connexion. Ce flux de code unique présente des avantages de sécurité par rapport à un flux pur côté serveur et par rapport à l'envoi de jetons d'accès à votre serveur.

Le flux de connexion permettant d'obtenir un jeton d'accès pour votre application côté serveur est illustré ci-dessous.

Les codes à usage unique présentent plusieurs avantages en termes de sécurité. Grâce aux codes, Google fournit des jetons directement à votre serveur, sans aucun intermédiaire. Bien que nous ne recommandions pas les fuites de code, elles sont très difficiles à utiliser sans votre code secret du client. Gardez votre client secret !

Implémenter le flux de code à usage unique

Le bouton Google Sign-In fournit à la fois un jeton d'accès et un code d'autorisation. Il s'agit d'un code unique que votre serveur peut échanger avec les serveurs de Google pour obtenir un jeton d'accès.

L'exemple de code suivant montre comment effectuer le flux de code unique.

Pour authentifier Google Sign-In à l'aide d'un flux de code unique, vous devez:

Étape 1: Créez un ID et un code secret du client

Pour créer un ID client et un code secret du client, créez un projet dans la console Google APIs, configurez un ID client OAuth et enregistrez vos origines JavaScript:

  1. Accédez à la console Google APIs.

  2. Dans la liste déroulante des projets, sélectionnez un projet existant ou créez-en un en sélectionnant Créer un projet.

  3. Dans la barre latérale, sous "API et services", sélectionnez Identifiants, puis cliquez sur Configurer l'écran d'autorisation.

    Choisissez une adresse e-mail, spécifiez un nom de produit, puis cliquez sur Enregistrer.

  4. Dans l'onglet Identifiants, sélectionnez la liste déroulante Créer des identifiants, puis choisissez ID client OAuth.

  5. Sous Type d'application, sélectionnez Application Web.

    Enregistrez les origines à partir desquelles votre application est autorisée à accéder aux API Google, comme suit. Une origine est une combinaison unique de protocole, d'hôte et de port.

    1. Dans le champ Origines JavaScript autorisées, saisissez l'origine de votre application. Vous pouvez indiquer plusieurs origines pour permettre à votre application de s'exécuter sur différents protocoles, domaines ou sous-domaines. Vous ne pouvez pas utiliser de caractères génériques. Dans l'exemple ci-dessous, la deuxième URL peut être une URL de production.

      http://localhost:8080
      https://myproductionurl.example.com
      
    2. Aucune valeur n'est requise dans le champ URI de redirection autorisé. Les URI de redirection ne sont pas utilisés avec les API JavaScript.

    3. Cliquez sur le bouton Créer.

  6. Dans la boîte de dialogue Client OAuth qui s'affiche, copiez l'ID client. L'ID client permet à votre application d'accéder aux API Google activées.

Étape 2: Incluez la bibliothèque de la plate-forme Google sur votre page

Incluez les scripts suivants, qui illustrent une fonction anonyme qui insère un script dans le DOM de cette page Web index.html.

<!-- The top of file index.html -->
<html itemscope itemtype="http://schema.org/Article">
<head>
  <!-- BEGIN Pre-requisites -->
  <script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js">
  </script>
  <script src="https://apis.google.com/js/client:platform.js?onload=start" async defer>
  </script>
  <!-- END Pre-requisites -->

Étape 3: Initialiser l'objet GoogleAuth

Chargez la bibliothèque auth2 et appelez gapi.auth2.init() pour initialiser l'objet GoogleAuth. Spécifiez votre ID client et les niveaux d'accès que vous souhaitez demander lorsque vous appelez init().

<!-- Continuing the <head> section -->
  <script>
    function start() {
      gapi.load('auth2', function() {
        auth2 = gapi.auth2.init({
          client_id: 'YOUR_CLIENT_ID.apps.googleusercontent.com',
          // Scopes to request in addition to 'profile' and 'email'
          //scope: 'additional_scope'
        });
      });
    }
  </script>
</head>
<body>
  <!-- ... -->
</body>
</html>

Étape 4: Ajoutez le bouton de connexion à votre page

Ajoutez le bouton de connexion à votre page Web et associez un gestionnaire de clics pour appeler grantOfflineAccess() afin de lancer le flux de code unique.

<!-- Add where you want your sign-in button to render -->
<!-- Use an image that follows the branding guidelines in a real app -->
<button id="signinButton">Sign in with Google</button>
<script>
  $('#signinButton').click(function() {
    // signInCallback defined in step 6.
    auth2.grantOfflineAccess().then(signInCallback);
  });
</script>

Étape 5: Connecter l'utilisateur

L'utilisateur clique sur le bouton de connexion et accorde à votre application les autorisations que vous avez demandées. La fonction de rappel que vous avez spécifiée dans la méthode grantOfflineAccess().then() reçoit ensuite un objet JSON avec un code d'autorisation. Exemple :

{"code":"4/yU4cQZTMnnMtetyFcIWNItG32eKxxxgXXX-Z4yyJJJo.4qHskT-UtugceFc0ZRONyF4z7U4UmAI"}

Étape 6: Envoyez le code d'autorisation au serveur

Le code correspond à votre code unique que votre serveur peut échanger contre son propre jeton d'accès et jeton d'actualisation. Vous ne pouvez obtenir un jeton d'actualisation qu'après avoir vu une boîte de dialogue d'autorisation demandant un accès hors connexion à l'utilisateur. Si vous avez spécifié le prompt select-account dans OfflineAccessOptions à l'étape 4, vous devez stocker le jeton d'actualisation que vous récupérez pour une utilisation ultérieure, car les échanges ultérieurs renvoient null pour le jeton d'actualisation. Ce flux offre une sécurité accrue par rapport à votre flux OAuth 2.0 standard.

Les jetons d'accès sont toujours renvoyés avec l'échange d'un code d'autorisation valide.

Le script suivant définit une fonction de rappel pour le bouton de connexion. Lorsqu'une connexion aboutit, la fonction stocke le jeton d'accès pour une utilisation côté client et envoie le code à usage unique à votre serveur situé sur le même domaine.

<!-- Last part of BODY element in file index.html -->
<script>
function signInCallback(authResult) {
  if (authResult['code']) {

    // Hide the sign-in button now that the user is authorized, for example:
    $('#signinButton').attr('style', 'display: none');

    // Send the code to the server
    $.ajax({
      type: 'POST',
      url: 'http://example.com/storeauthcode',
      // Always include an `X-Requested-With` header in every AJAX request,
      // to protect against CSRF attacks.
      headers: {
        'X-Requested-With': 'XMLHttpRequest'
      },
      contentType: 'application/octet-stream; charset=utf-8',
      success: function(result) {
        // Handle or verify the server response.
      },
      processData: false,
      data: authResult['code']
    });
  } else {
    // There was an error.
  }
}
</script>

Étape 7: Échangez le code d'autorisation contre un jeton d'accès

Sur le serveur, échangez le code d'autorisation contre des jetons d'accès et d'actualisation. Utilisez le jeton d'accès pour appeler les API Google au nom de l'utilisateur et, éventuellement, stocker le jeton d'actualisation pour en obtenir un nouveau lorsque le jeton d'accès expire.

Si vous avez demandé l'accès au profil, vous obtenez également un jeton d'ID contenant les informations de base du profil de l'utilisateur.

Exemple :

Java
// (Receive authCode via HTTPS POST)


if (request.getHeader("X-Requested-With") == null) {
  // Without the `X-Requested-With` header, this request could be forged. Aborts.
}

// Set path to the Web application client_secret_*.json file you downloaded from the
// Google API Console: https://console.cloud.google.com/apis/credentials
// You can also find your Web application client ID and client secret from the
// console and specify them directly when you create the GoogleAuthorizationCodeTokenRequest
// object.
String CLIENT_SECRET_FILE = "/path/to/client_secret.json";

// Exchange auth code for access token
GoogleClientSecrets clientSecrets =
    GoogleClientSecrets.load(
        JacksonFactory.getDefaultInstance(), new FileReader(CLIENT_SECRET_FILE));
GoogleTokenResponse tokenResponse =
          new GoogleAuthorizationCodeTokenRequest(
              new NetHttpTransport(),
              JacksonFactory.getDefaultInstance(),
              "https://oauth2.googleapis.com/token",
              clientSecrets.getDetails().getClientId(),
              clientSecrets.getDetails().getClientSecret(),
              authCode,
              REDIRECT_URI)  // Specify the same redirect URI that you use with your web
                             // app. If you don't have a web version of your app, you can
                             // specify an empty string.
              .execute();

String accessToken = tokenResponse.getAccessToken();

// Use access token to call API
GoogleCredential credential = new GoogleCredential().setAccessToken(accessToken);
Drive drive =
    new Drive.Builder(new NetHttpTransport(), JacksonFactory.getDefaultInstance(), credential)
        .setApplicationName("Auth Code Exchange Demo")
        .build();
File file = drive.files().get("appfolder").execute();

// Get profile info from ID token
GoogleIdToken idToken = tokenResponse.parseIdToken();
GoogleIdToken.Payload payload = idToken.getPayload();
String userId = payload.getSubject();  // Use this value as a key to identify a user.
String email = payload.getEmail();
boolean emailVerified = Boolean.valueOf(payload.getEmailVerified());
String name = (String) payload.get("name");
String pictureUrl = (String) payload.get("picture");
String locale = (String) payload.get("locale");
String familyName = (String) payload.get("family_name");
String givenName = (String) payload.get("given_name");
Python
from apiclient import discovery
import httplib2
from oauth2client import client

# (Receive auth_code by HTTPS POST)


# If this request does not have `X-Requested-With` header, this could be a CSRF
if not request.headers.get('X-Requested-With'):
    abort(403)

# Set path to the Web application client_secret_*.json file you downloaded from the
# Google API Console: https://console.cloud.google.com/apis/credentials
CLIENT_SECRET_FILE = '/path/to/client_secret.json'

# Exchange auth code for access token, refresh token, and ID token
credentials = client.credentials_from_clientsecrets_and_code(
    CLIENT_SECRET_FILE,
    ['https://www.googleapis.com/auth/drive.appdata', 'profile', 'email'],
    auth_code)

# Call Google API
http_auth = credentials.authorize(httplib2.Http())
drive_service = discovery.build('drive', 'v3', http=http_auth)
appfolder = drive_service.files().get(fileId='appfolder').execute()

# Get profile info from ID token
userid = credentials.id_token['sub']
email = credentials.id_token['email']