Procéder à la connexion des utilisateurs sur App Engine


Ce tutoriel explique comment récupérer, valider et stocker les identifiants tiers à l'aide d'Identity Platform, de l'environnement standard App Engine et de Datastore.

Ce document décrit une application de prise de notes simple appelée "Firenotes", qui stocke les notes des utilisateurs dans leurs notebooks personnels. Les notebooks sont stockés par utilisateur et identifiés par l'ID Identity Platform unique de chaque utilisateur. L'application est constituée des composants suivants :

  • L'interface configure l'interface utilisateur de connexion et récupère l'ID Identity Platform. Il gère également les changements d'état d'authentification et permet aux utilisateurs de voir leurs notes.

  • FirebaseUI est une solution Open Source prête à l'emploi qui simplifie les tâches d'authentification et d'UI. Le SDK gère la connexion des utilisateurs, associe plusieurs fournisseurs à un compte, récupère les mots de passe, et bien plus encore. Il met en œuvre les meilleures pratiques d'authentification pour une expérience de connexion fluide et sécurisée.

  • Le serveur vérifie l'état d'authentification de l'utilisateur et renvoie les informations du profil utilisateur, ainsi que les notes de l'utilisateur.

L'application stocke les identifiants utilisateur dans Datastore à l'aide de la bibliothèque cliente NDB, mais vous pouvez stocker les identifiants dans la base de données de votre choix.

L'application Firenotes est basée sur le framework d'application Web Flask. L'exemple d'application utilise Flask en raison de sa simplicité et de sa facilité d'utilisation, mais les concepts et les technologies explorés sont applicables quel que soit le framework que vous utilisez.

Objectifs

Dans ce tutoriel, vous réaliserez les tâches suivantes :

  • Configurer l'interface utilisateur avec FirebaseUI pour Identity Platform.
  • Obtenir un jeton d'ID Identity Platform et le vérifier à l'aide de l'authentification côté serveur.
  • Stocker les identifiants utilisateur et les données associées dans Datastore.
  • Interroger une base de données à l'aide de la bibliothèque cliente NDB.
  • Déployer une application sur App Engine.

Coûts

Ce tutoriel utilise des composants facturables de Google Cloud, dont :

  • Datastore
  • Identity Platform

Obtenez une estimation des coûts en fonction de votre utilisation prévue à l'aide du simulateur de coût. Les nouveaux utilisateurs de Google Cloud peuvent bénéficier d'un essai gratuit.

Avant de commencer

  1. Installez Git, Python 2.7 et virtualenv. Pour en savoir plus sur la configuration de votre environnement de développement Python, par exemple sur l'installation de la dernière version de Python, consultez la page Configurer un environnement de développement Python pour Google Cloud.
  2. Connectez-vous à votre compte Google Cloud. Si vous débutez sur Google Cloud, créez un compte pour évaluer les performances de nos produits en conditions réelles. Les nouveaux clients bénéficient également de 300 $ de crédits gratuits pour exécuter, tester et déployer des charges de travail.
  3. Dans Google Cloud Console, sur la page de sélection du projet, sélectionnez ou créez un projet Google Cloud.

    Accéder au sélecteur de projet

  4. Installez Google Cloud CLI.
  5. Pour initialiser gcloudCLI, exécutez la commande suivante :

    gcloud init
  6. Dans Google Cloud Console, sur la page de sélection du projet, sélectionnez ou créez un projet Google Cloud.

    Accéder au sélecteur de projet

  7. Installez Google Cloud CLI.
  8. Pour initialiser gcloudCLI, exécutez la commande suivante :

    gcloud init

Si vous avez déjà installé et initialisé le SDK dans un autre projet, définissez le projet gcloud sur l'ID de projet App Engine que vous utilisez pour Firenotes. Consultez la page Gérer les configurations du SDK Google Cloud pour voir des commandes spécifiques permettant de mettre à jour un projet avec l'outil gcloud.

Cloner l'exemple d'application

Pour télécharger l'exemple sur l'ordinateur local :

  1. Clonez le dépôt de l'exemple d'application sur votre ordinateur local :

    git clone https://github.com/GoogleCloudPlatform/python-docs-samples.git

    Vous pouvez également télécharger l'exemple en tant que fichier zip et l'extraire.

  2. Accédez au répertoire qui contient l'exemple de code :

    cd python-docs-samples/appengine/standard/firebase/firenotes
    

Ajouter l'interface utilisateur

Pour configurer FirebaseUI pour Identity Platform et activer les fournisseurs d'identité, procédez comme suit :

  1. Ajoutez Identity Platform à votre application en procédant comme suit :

    1. Accédez à Google Cloud Console.
      Accéder à la console Google Cloud
    2. Sélectionnez le projet Google Cloud que vous souhaitez utiliser :
      • Si vous disposez déjà d'un projet, sélectionnez-le dans la liste déroulante Sélectionner une organisation en haut de la page.
      • Si vous n'avez pas encore de projet Google Cloud, créez-en un dans la console Google Cloud.
    3. Accédez à la page Identity Platform Marketplace dans la console Google Cloud.
      Accéder à la page Identity Platform Marketplace
    4. Sur la page Identity Platform Marketplace, cliquez sur Enable Customer Identity (Activer l'identité du client).
    5. Accédez à la page Utilisateurs de l'identité client dans la console Google Cloud.
      Accéder à la page Utilisateurs
    6. En haut à droite, cliquez sur Informations sur la configuration de l'application.
    7. Copiez les informations sur la configuration de l'application dans votre application Web.

      // Obtain the following from the "Add Firebase to your web app" dialogue
      // Initialize Firebase
      var config = {
        apiKey: "<API_KEY>",
        authDomain: "<PROJECT_ID>.firebaseapp.com",
        databaseURL: "https://<DATABASE_NAME>.firebaseio.com",
        projectId: "<PROJECT_ID>",
        storageBucket: "<BUCKET>.appspot.com",
        messagingSenderId: "<MESSAGING_SENDER_ID>"
      };
  2. Modifiez le fichier backend/app.yaml et saisissez l'ID de votre projet Google Cloud dans les variables d'environnement :

    # Copyright 2021 Google LLC
    #
    # Licensed under the Apache License, Version 2.0 (the "License");
    # you may not use this file except in compliance with the License.
    # You may obtain a copy of the License at
    #
    #      http://www.apache.org/licenses/LICENSE-2.0
    #
    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS,
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    # See the License for the specific language governing permissions and
    # limitations under the License.
    
    runtime: python27
    api_version: 1
    threadsafe: true
    service: backend
    
    handlers:
    - url: /.*
      script: main.app
    
    env_variables:
      GAE_USE_SOCKETS_HTTPLIB : 'true'
    
  3. Dans le fichier frontend/main.js, configurez le widget de connexion FirebaseUI en sélectionnant les fournisseurs que vous souhaitez proposer à vos utilisateurs.

    // Firebase log-in widget
    function configureFirebaseLoginWidget() {
      var uiConfig = {
        'signInSuccessUrl': '/',
        'signInOptions': [
          // Leave the lines as is for the providers you want to offer your users.
          firebase.auth.GoogleAuthProvider.PROVIDER_ID,
          firebase.auth.FacebookAuthProvider.PROVIDER_ID,
          firebase.auth.TwitterAuthProvider.PROVIDER_ID,
          firebase.auth.GithubAuthProvider.PROVIDER_ID,
          firebase.auth.EmailAuthProvider.PROVIDER_ID
        ],
        // Terms of service url
        'tosUrl': '<your-tos-url>',
      };
    
      var ui = new firebaseui.auth.AuthUI(firebase.auth());
      ui.start('#firebaseui-auth-container', uiConfig);
    }
  4. Dans la console Google Cloud, activez les fournisseurs que vous souhaitez conserver:

    1. Accédez à la page Fournisseurs d'identité client dans la console Google Cloud.
      Accéder à la page Fournisseurs
    2. Cliquez sur Ajouter un fournisseur.
    3. Dans la liste déroulante Sélectionner un fournisseur, sélectionnez les fournisseurs que vous souhaitez utiliser.
    4. À côté d'Activé, cliquez sur le bouton pour activer le fournisseur.
      • Pour les fournisseurs d'identité tiers, saisissez l'ID du fournisseur et sa clé secrète à partir de son site pour les développeurs. Les documents Firebase donnent à ce sujet des instructions spécifiques dans les sections "Avant de commencer" des guides Facebook, Twitter et GitHub.
      • Pour les intégrations SAML et OIDC, reportez-vous à la configuration effectuée sur le site de votre fournisseur d'identité.
  5. Ajoutez votre domaine à la liste des domaines autorisés dans Identity Platform :

    1. Accédez à la page Paramètres de l'identité client dans la console Google Cloud.
      Accéder à la page Paramètres
    2. Dans la section Authorized Domains (Domaines autorisés), cliquez sur Add Domain (Ajouter un domaine).
    3. Saisissez le domaine de votre application en respectant le format suivant :

      [PROJECT_ID].appspot.com
      

      N'incluez pas http:// avant le nom de domaine.

Installer les dépendances

  1. Accédez au répertoire backend et terminez la configuration de l'application comme suit :

    cd backend/
    
  2. Installez les dépendances dans un répertoire lib de votre projet :

    pip install -t lib -r requirements.txt
    
  3. Dans appengine_config.py, la méthode vendor.add() enregistre les bibliothèques dans le répertoire lib.

Exécuter votre application en local

Pour exécuter l'application en local, utilisez le serveur de développement local App Engine :

  1. Ajoutez l'URL suivante en tant que backendHostURL dans main.js :

    http://localhost:8081

  2. Accédez au répertoire racine de l'application. Ensuite, démarrez le serveur de développement :

    dev_appserver.py frontend/app.yaml backend/app.yaml
    
  3. Visitez http://localhost:8080/ dans un navigateur Web.

Authentifier des utilisateurs auprès du serveur

Maintenant que vous avez configuré un projet et initialisé une application pour le développement, vous pouvez parcourir le code pour comprendre comment récupérer et vérifier les jetons d'ID Identity Platform sur le serveur.

Obtenir un jeton d'ID depuis Identity Platform

La première étape de l'authentification côté serveur consiste à récupérer un jeton d'accès à vérifier. Les requêtes d'authentification sont traitées avec l'écouteur onAuthStateChanged() d'Identity Platform :

firebase.auth().onAuthStateChanged(function(user) {
  if (user) {
    $('#logged-out').hide();
    var name = user.displayName;

    /* If the provider gives a display name, use the name for the
    personal welcome message. Otherwise, use the user's email. */
    var welcomeName = name ? name : user.email;

    user.getIdToken().then(function(idToken) {
      userIdToken = idToken;

      /* Now that the user is authenicated, fetch the notes. */
      fetchNotes();

      $('#user').text(welcomeName);
      $('#logged-in').show();

    });

  } else {
    $('#logged-in').hide();
    $('#logged-out').show();

  }
});

Lorsqu'un utilisateur est connecté, la méthode getToken() d'Identity Platform du rappel renvoie un jeton d'ID Identity Platform sous la forme d'un jeton Web JSON (JWT, JSON Web Token).

Vérifier des jetons sur le serveur

Une fois qu'un utilisateur s'est connecté, le service d'interface récupère toutes les notes existantes dans le notebook de l'utilisateur via une requête AJAX GET. Cela nécessite une autorisation pour accéder aux données de l'utilisateur. Le jeton JWT est donc envoyé dans l'en-tête Authorization de la requête avec le schéma Bearer :

// Fetch notes from the backend.
function fetchNotes() {
  $.ajax(backendHostUrl + '/notes', {
    /* Set header for the XMLHttpRequest to get data from the web server
    associated with userIdToken */
    headers: {
      'Authorization': 'Bearer ' + userIdToken
    }
  })

Votre serveur doit vérifier que le jeton est signé par Identity Platform avant que le client ne puisse accéder aux données du serveur. Vous pouvez vérifier ce jeton à l'aide de la bibliothèque d'authentification Google pour Python. Utilisez la fonction verify_firebase_token de la bibliothèque d'authentification pour valider le jeton de support et extraire les revendications :

id_token = request.headers["Authorization"].split(" ").pop()
claims = google.oauth2.id_token.verify_firebase_token(
    id_token, HTTP_REQUEST, audience=os.environ.get("GOOGLE_CLOUD_PROJECT")
)
if not claims:
    return "Unauthorized", 401

Chaque fournisseur d'identité envoie un ensemble différent de revendications. Chaque ensemble comporte au moins une revendication sub avec un ID utilisateur unique et une revendication fournissant certaines informations de profil, telles que name ou email, dont vous pouvez vous servir pour personnaliser l'expérience utilisateur sur votre application.

Gérer les données utilisateur dans Datastore

Après avoir authentifié un utilisateur, vous devez stocker ses données pour qu'elles puissent persister une fois la session ouverte terminée. Les sections suivantes expliquent comment stocker une note en tant qu'entité Datastore et séparer les entités par ID utilisateur.

Créer des entités pour stocker des données utilisateur

Vous pouvez créer une entité dans Datastore en déclarant une classe de modèle NDB avec certaines propriétés, telles que des entiers ou des chaînes. Datastore indexe les entités par genre. Dans le cas de l'application Firenotes, le genre de chaque entité est Note. À des fins d'interrogation, chaque Note est stockée avec un nom de clé qui correspond à l'ID utilisateur obtenu à partir de la revendication sub de la section précédente.

Le code suivant montre comment définir les propriétés d'une entité, lors de la création de l'entité avec la méthode constructeur de la classe de modèle et après sa création via l'affectation de propriétés individuelles :

data = request.get_json()

# Populates note properties according to the model,
# with the user ID as the key name.
note = Note(parent=ndb.Key(Note, claims["sub"]), message=data["message"])

# Some providers do not provide one of these so either can be used.
note.friendly_id = claims.get("name", claims.get("email", "Unknown"))

Pour écrire la nouvelle Note dans Datastore, appelez la méthode put() sur l'objet note.

Récupérer des données utilisateur

Pour récupérer les données utilisateur associées à un ID utilisateur particulier, appelez la méthode query() de la bibliothèque NDB pour rechercher dans la base de données des notes appartenant au même groupe d'entités. Les entités du même groupe, ou ayant le même chemin d'ancêtre, partagent un nom de clé commun, qui correspond dans ce cas à l'ID utilisateur.

def query_database(user_id):
    """Fetches all notes associated with user_id.

    Notes are ordered them by date created, with most recent note added
    first.
    """
    ancestor_key = ndb.Key(Note, user_id)
    query = Note.query(ancestor=ancestor_key).order(-Note.created)
    notes = query.fetch()

    note_messages = []

    for note in notes:
        note_messages.append(
            {
                "friendly_id": note.friendly_id,
                "message": note.message,
                "created": note.created,
            }
        )

    return note_messages

Vous pouvez ensuite récupérer les données de requête et afficher les notes dans le client :

// Fetch notes from the backend.
function fetchNotes() {
  $.ajax(backendHostUrl + '/notes', {
    /* Set header for the XMLHttpRequest to get data from the web server
    associated with userIdToken */
    headers: {
      'Authorization': 'Bearer ' + userIdToken
    }
  }).then(function(data){
    $('#notes-container').empty();
    // Iterate over user data to display user's notes from database.
    data.forEach(function(note){
      $('#notes-container').append($('<p>').text(note.message));
    });
  });
}

Déployer l'application

Vous avez intégré Identity Platform à votre application App Engine. Pour voir votre application s'exécuter dans un environnement de production en temps réel, procédez comme suit :

  1. Remplacez l'URL de l'hôte de backend dans le fichier main.js par https://backend-dot-[PROJECT_ID].appspot.com. Remplacez [PROJECT_ID] par l'ID du projet.
  2. Déployez l'application à l'aide de l'interface de ligne de commande du SDK Google Cloud :

    gcloud app deploy backend/index.yaml frontend/app.yaml backend/app.yaml
    
  3. Consultez l'application en ligne à l'adresse https://[PROJECT_ID].appspot.com.

Effectuer un nettoyage

Pour éviter que les ressources utilisées dans ce tutoriel soient facturées sur votre compte Google Cloud, supprimez votre projet App Engine comme suit :

Supprimer le projet

Le moyen le plus simple d'empêcher la facturation est de supprimer le projet que vous avez créé pour ce tutoriel.

Pour supprimer le projet :

  1. Dans la console Google Cloud, accédez à la page Gérer les ressources.

    Accéder à la page Gérer les ressources

  2. Dans la liste des projets, sélectionnez le projet que vous souhaitez supprimer, puis cliquez sur Supprimer.
  3. Dans la boîte de dialogue, saisissez l'ID du projet, puis cliquez sur Arrêter pour supprimer le projet.

Étapes suivantes

  • Découvrez des architectures de référence, des schémas et des bonnes pratiques concernant Google Cloud. Consultez notre Centre d'architecture cloud.