Authentifier des utilisateurs sur App Engine à l'aide de Firebase

Ce tutoriel explique comment récupérer, vérifier et stocker des identifiants utilisateur à l'aide de Firebase Authentication, de l'environnement standard de Google App Engine et de Google Cloud Datastore.

Le document décrit une application de prise de notes simple appelée "Firenotes", qui stocke les notes des utilisateurs dans leurs ordinateurs ultraportables personnels. Les ordinateurs ultraportables sont stockés par utilisateur et identifiés par l'identifiant Firebase Authentication unique de chaque utilisateur. L'application est constituée des composants suivants :

  • Le frontend configure l'interface utilisateur de connexion et récupère l'identifiant Firebase Authentication. Il gère également les changements d'état d'authentification et permet aux utilisateurs de voir leurs notes.

    #NOTYPO
  • FirebaseUI est une solution Open Source prête à l'emploi, qui gère la connexion des utilisateurs, la liaison de plusieurs fournisseurs à un seul compte, la récupération des mots de passe, et bien plus encore. Elle met en œuvre les meilleures pratiques d'authentification pour une expérience de connexion fluide et sécurisée.

    FirebaseUI

  • 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.

    #NOTYPO

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

Le diagramme suivant représente la communication entre l'interface et le serveur ainsi que le transfert des identifiants utilisateur depuis Firebase vers la base de données.

Schéma de l'architecture

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

  • Configurer l'interface utilisateur de Firebase Authentication.
  • Obtenir un jeton d'identification Firebase et le vérifier à l'aide de l'authentification côté serveur.
  • #NOTYPO
  • Stocker les identifiants utilisateur et les données associées dans Cloud 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 fait appel à des composants facturables de Cloud Platform, ce qui inclut :

  • Google Cloud Datastore

Obtenez une estimation des coûts en fonction de votre utilisation prévue à l'aide du simulateur de coût. Les nouveaux utilisateurs de Cloud Platform 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 comment installer la dernière version de Python, consultez la page Configurer un environnement de développement Python pour Google Cloud Platform.
  2. Connectez-vous à votre compte Google.

    Si vous n'en possédez pas déjà un, vous devez en créer un.

  3. Sélectionnez ou créez un projet Google Cloud Platform.

    Accéder à la page "Gérer les ressources"

  4. Installez et initialisez le SDK Cloud.

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 Cloud pour 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 de Firebase Authentication

Pour configurer FirebaseUI et activer les fournisseurs d'identité :

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

    1. Créez un projet Firebase dans la console Firebase.
      • Si vous ne disposez d'aucun projet Firebase, cliquez sur Ajouter un projet et entrez un nom de projet Google Cloud Platform existant ou un nouveau nom de projet.
      • #NOTYPO
      • Si vous souhaitez utiliser un projet Firebase existant, sélectionnez-le dans la console.
      • #NOTYPO
    2. Dans la page de présentation du projet, cliquez sur Ajouter Firebase à votre application Web. Si votre projet contient déjà une application, sélectionnez Ajouter une application dans la page de présentation du projet.
    3. Utilisez la section Initialize Firebase de l'extrait de code personnalisé de votre projet pour remplir la section suivante du fichier frontend/main.js :

      // 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 Firebase dans les variables d'environnement :

    runtime: python27
    api_version: 1
    threadsafe: true
    service: backend
    
    handlers:
    - url: /.*
      script: main.app
    
    env_variables:
      # Replace with your Firebase project ID.
      FIREBASE_PROJECT_ID: '<PROJECT_ID>'
    

  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,#NOTYPO
          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. Activez les fournisseurs que vous souhaitez conserver dans la console Firebase en cliquant sur Authentication (Authentification) > Sign-in method (Mode de connexion). Ensuite, sous Sign-in providers (Fournisseurs de connexion), placez le curseur sur un fournisseur, puis cliquez sur l'icône en forme de crayon.

    Fournisseurs de connexion

    1. Activez ou désactivez le bouton Enable (Activer). Saisissez l'ID du fournisseur d'identité tiers et son code secret à 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. Après avoir activé un fournisseur, cliquez sur Save (Enregistrer).

      Bouton d'activation/de désactivation

    2. Dans la console Firebase, sous Domaines autorisés, cliquez sur Ajouter un domaine et saisissez le domaine de votre application sur App Engine au format suivant :

      [PROJECT_ID].appspot.com
      

      Veillez à ne pas insérer http:// avant le nom de domaine.

Installer les dépendances

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

macOS/Linux

  1. Créez un environnement Python isolé dans un répertoire externe à votre projet et activez-le :
    virtualenv env
    source env/bin/activate
  2. Accédez au répertoire de votre projet et installez des dépendances :
    cd YOUR_PROJECT
    pip install -t lib -r requirements.txt

Windows

Si vous avez installé le SDK Cloud, vous devez disposer de Python 2.7, généralement disponible sous C:\python27_x64\ (pour les systèmes 64 bits). Exécutez les packages Python à l'aide de PowerShell.

  1. Localisez votre installation de PowerShell.
  2. Cliquez avec le bouton droit sur le raccourci PowerShell et démarrez une session en tant qu'administrateur.
  3. Essayez d'exécuter la commande python. Si le dossier Python est introuvable, ajoutez-le à la variable d'environnement PATH.
    $env:Path += ";C:\python27_x64\"
  4. Créez un environnement Python isolé dans un répertoire externe à votre projet et activez-le :
    python -m virtualenv env
    env\Scripts\activate
  5. Accédez au répertoire de votre projet et installez des dépendances :
    cd YOUR_PROJECT
    python -m pip install -t lib -r requirements.txt

Dans le fichier 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 qu'url backendHostURL dans le fichier 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'identification Firebase sur le serveur.

Obtenir un jeton d'identification de Firebase

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() de Firebase :

#NOTYPO
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.getToken().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() de Firebase dans le rappel renvoie un jeton d'identification Firebase sous la forme d'un jeton Web JSON (JWT).

Vérifier des jetons sur le serveur

Une fois qu'un utilisateur s'est connecté, le service frontend récupère toutes les notes existantes dans l'ordinateur ultraportable de l'utilisateur via une requête AJAX GET. Cela nécessite une autorisation pour accéder aux données de l'utilisateur. Le JWT est donc envoyé dans l'en-tête Authorization de la requête à l'aide du 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
    }
  })

Le serveur doit vérifier que le jeton est signé par Firebase 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 vérifier 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)
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 identifiant 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 des données utilisateur dans Cloud 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é Cloud Datastore et séparer les entités par identifiant utilisateur.

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

Vous pouvez créer une entité dans Cloud Datastore en déclarant une classe de modèle NDB avec certaines propriétés, telles que des entiers ou des chaînes. Cloud Datastore indexe les entités par genre. Dans le cas de 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'identifiant 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 Note nouvellement créée dans Cloud 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 identifiant 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'identifiant 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 votre application

Vous avez intégré avec succès Firebase Authentication à 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 du serveur hôte dans le fichier main.js par https://backend-dot-[PROJECT_ID].appspot.com. Remplacez [PROJECT_ID] par l'ID de votre projet.
  2. Déployez l'application à l'aide de l'interface de ligne de commande du SDK Cloud :

    gcloud app deploy backend/index.yaml frontend/app.yaml backend/app.yaml
    
  3. Visualisez l'application en temps réel à l'adresse https://[PROJECT_ID].appspot.com.

Nettoyer

Afin d'éviter la facturation sur votre compte Google Cloud Platform des ressources utilisées dans ce tutoriel, procédez comme suit :

Supprimer le projet

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

Pour supprimer le projet, procédez comme suit :

  1. Dans la console GCP, accédez à la page "Projets".

    Accéder à la page Projets

  2. Dans la liste des projets, sélectionnez celui 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

Cette page vous a-t-elle été utile ? Évaluez-la :

Envoyer des commentaires concernant…

Environnement standard App Engine pour Python