Accesso degli utenti su App Engine


Questo tutorial mostra come recuperare, verificare e archiviare le credenziali di terze parti utilizzando Identity Platform, l'ambiente standard di App Engine e Datastore.

Questo documento illustra una semplice applicazione per prendere appunti chiamata Firenotes, che archivia le note degli utenti nei loro blocchi note personali. Notebooks vengono archiviati per utente e identificati dall'ID univoco di Identity Platform di ciascun utente. L'applicazione contiene i seguenti componenti:

  • Il frontend configura l'interfaccia utente di accesso e recupera l'ID piattaforma di Identity. Gestisce inoltre le modifiche dello stato di autenticazione e consente agli utenti di vedere le loro note.

  • FirebaseUI è una soluzione open source disponibile che semplifica l'autenticazione e le attività UI. L'SDK gestisce l'accesso degli utenti, collegando più provider a un account, recuperando le password e altro ancora. Implementa le best practice di autenticazione per un'esperienza di accesso fluida e sicura.

  • Il backend verifica lo stato di autenticazione dell'utente e restituisce le informazioni del profilo utente e le note dell'utente.

L'applicazione archivia le credenziali utente in Datastore utilizzando la libreria client NDB, ma puoi archiviare le credenziali in un database a tua scelta.

Firenotes si basa sul framework di applicazioni web Flask. L'app di esempio utilizza Flask per la sua semplicità e facilità d'uso, ma i concetti e le tecnologie esplorati sono applicabili indipendentemente dal framework utilizzato.

Obiettivi

Completando questo tutorial, potrai realizzare quanto segue:

  • Configura l'interfaccia utente con FirebaseUI per Identity Platform.
  • Ottenere un token ID piattaforma di identità e verificarlo utilizzando l'autenticazione lato server.
  • Archiviare le credenziali utente e i dati associati in Datastore.
  • Esegui una query su un database utilizzando la libreria client NDB.
  • Eseguire il deployment di un'app in App Engine.

Costi

Questo tutorial utilizza i componenti fatturabili di Google Cloud, tra cui:

  • Datastore
  • Identity Platform

Utilizza il Calcolatore prezzi per generare una stima dei costi in base all'utilizzo previsto. I nuovi utenti di Google Cloud potrebbero essere idonei per una prova gratuita.

Prima di iniziare

  1. Installa Git, Python 2.7 e virtualenv. Per saperne di più sulla configurazione dell'ambiente di sviluppo Python, ad esempio sull'installazione della versione più recente di Python, consulta la sezione Configurare un ambiente di sviluppo Python per Google Cloud.
  2. Accedi al tuo account Google Cloud. Se non conosci Google Cloud, crea un account per valutare le prestazioni dei nostri prodotti in scenari reali. I nuovi clienti ricevono anche 300 $di crediti gratuiti per l'esecuzione, il test e il deployment dei carichi di lavoro.
  3. Nella pagina del selettore di progetti della console Google Cloud, seleziona o crea un progetto Google Cloud.

    Vai al selettore progetti

  4. Installa Google Cloud CLI.
  5. Per initialize gcloud CLI, esegui questo comando:

    gcloud init
  6. Nella pagina del selettore di progetti della console Google Cloud, seleziona o crea un progetto Google Cloud.

    Vai al selettore progetti

  7. Installa Google Cloud CLI.
  8. Per initialize gcloud CLI, esegui questo comando:

    gcloud init

Se hai già installato e inizializzato l'SDK in un altro progetto, imposta il progetto gcloud sull'ID progetto App Engine che stai utilizzando per Firenotes. Consulta Gestione delle configurazioni di Google Cloud SDK per i comandi specifici che consentono di aggiornare un progetto con lo strumento gcloud.

Clonazione dell'app di esempio

Per scaricare l'esempio sulla tua macchina locale:

  1. Clona il repository dell'applicazione di esempio sulla tua macchina locale:

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

    In alternativa, puoi scaricare l'esempio come file ZIP ed estrarlo.

  2. Vai alla directory che contiene il codice campione:

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

Aggiunta dell'interfaccia utente

Per configurare FirebaseUI per Identity Platform e abilitare i provider di identità:

  1. Aggiungi Identity Platform alla tua app seguendo questa procedura:

    1. Vai alla console Google Cloud.
      Vai alla console Google Cloud
    2. Seleziona il progetto Google Cloud che vuoi utilizzare:
      • Se esiste già un progetto, selezionalo nell'elenco a discesa Seleziona organizzazione nella parte superiore della pagina.
      • Se non hai un progetto Google Cloud esistente, creane uno nuovo nella console Google Cloud.
    3. Vai alla pagina Identity Platform Marketplace nella console Google Cloud.
      Vai alla pagina Marketplace di Identity Platform
    4. Nella pagina Identity Platform Marketplace, fai clic su Abilita Customer Identity.
    5. Vai alla pagina Utenti di Customer Identity nella console Google Cloud.
      Vai alla pagina Utenti
    6. In alto a destra, fai clic su Dettagli di configurazione dell'applicazione.
    7. Copia i dettagli di configurazione dell'applicazione nella tua applicazione 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. Modifica il file backend/app.yaml e inserisci il tuo ID progetto Google Cloud nelle variabili di ambiente:

    # 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. Nel file frontend/main.js, configura il widget di accesso FirebaseUI selezionando i provider che vuoi offrire ai tuoi utenti.

    // 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. Nella console Google Cloud, abilita i provider che hai scelto di mantenere:

    1. Vai alla pagina Provider di identità cliente nella console Google Cloud.
      Vai alla pagina Provider
    2. Fai clic su Add A Provider (Aggiungi un provider).
    3. Nell'elenco a discesa Seleziona un provider, scegli i provider che vuoi utilizzare.
    4. Accanto ad Enabled (Attivato), fai clic sul pulsante per abilitare il provider.
      • Per i provider di identità di terze parti, inserisci l'ID provider e il secret dal sito per sviluppatori del provider. I documenti di Firebase forniscono istruzioni specifiche nelle sezioni "Prima di iniziare" delle guide di Facebook, Twitter e GitHub.
      • Per le integrazioni SAML e OIDC, fai riferimento alla configurazione del tuo IdP.
  5. Aggiungi il tuo dominio all'elenco dei domini autorizzati in Identity Platform:

    1. Vai alla pagina Impostazioni dell'identità del cliente nella console Google Cloud.
      Vai alla pagina Impostazioni
    2. In Authorized Domains (Domini autorizzati), fai clic su Add Domain (Aggiungi dominio).
    3. Inserisci il dominio della tua app nel seguente formato:

      [PROJECT_ID].appspot.com
      

      Non includere http:// prima del nome di dominio.

Installazione delle dipendenze

  1. Passa alla directory backend e completa la configurazione dell'applicazione:

    cd backend/
    
  2. Installa le dipendenze in una directory lib nel progetto:

    pip install -t lib -r requirements.txt
    
  3. In appengine_config.py, il metodo vendor.add() registra le librerie nella directory lib.

Esecuzione dell'applicazione in locale

Per eseguire l'applicazione localmente, utilizza il server di sviluppo locale di App Engine:

  1. Aggiungi il seguente URL come backendHostURL in main.js:

    http://localhost:8081

  2. Vai alla directory principale dell'applicazione. Avvia quindi il server di sviluppo:

    dev_appserver.py frontend/app.yaml backend/app.yaml
    
  3. Visita la pagina http://localhost:8080/ su un browser web.

Autenticazione degli utenti sul server

Ora che hai configurato un progetto e inizializzato un'applicazione per lo sviluppo, esamina il codice per capire come recuperare e verificare i token ID Identity Platform sul server.

Richiedere un token ID da Identity Platform

Il primo passaggio nell'autenticazione lato server consiste nel recuperare un token di accesso per la verifica. Le richieste di autenticazione vengono gestite con l'ascoltatore onAuthStateChanged() di 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();

  }
});

Quando un utente esegue l'accesso, il metodo getToken() di Identity Platform nel callback restituisce un token ID piattaforma di identità sotto forma di token JWT (JSON Web Token).

Verifica dei token sul server

Dopo che un utente ha eseguito l'accesso, il servizio frontend recupera eventuali note esistenti nel suo blocco note tramite una richiesta GET AJAX. Ciò richiede l'autorizzazione ad accedere ai dati dell'utente, quindi il JWT viene inviato nell'intestazione Authorization della richiesta utilizzando lo schema 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
    }
  })

Prima che il client possa accedere ai dati del server, il server deve verificare che il token sia firmato da Identity Platform. Puoi verificare questo token utilizzando la libreria di autenticazione di Google per Python. Usa la funzione verify_firebase_token della libreria di autenticazione per verificare il token di connessione ed estrarre le attestazioni:

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

Ogni provider di identità invia un diverso insieme di rivendicazioni, ma ciascuna ha almeno una rivendicazione sub con un ID utente univoco e una rivendicazione che fornisce alcune informazioni del profilo, come name o email, che puoi utilizzare per personalizzare l'esperienza utente nella tua app.

Gestione dei dati utente in Datastore

Dopo l'autenticazione di un utente, devi archiviare i suoi dati in modo che vengano conservati al termine di una sessione di accesso. Le sezioni seguenti spiegano come archiviare una nota come entità Datastore e separare le entità in base all'ID utente.

Creazione di entità per archiviare i dati utente

È possibile creare un'entità in Datastore dichiarando una classe del modello NDB con determinate proprietà, come numeri interi o stringhe. Datastore indicizza le entità per kind; nel caso di Firenotes, il tipo di ciascuna entità è Note. Ai fini delle query, ogni Note viene archiviato con un nome chiave, che corrisponde all'ID utente ottenuto dall'attestazione sub della sezione precedente.

Il codice seguente mostra come impostare le proprietà di un'entità, sia con il metodo del costruttore per la classe del modello quando viene creata l'entità sia tramite l'assegnazione di singole proprietà dopo la creazione:

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

Per scrivere il valore Note appena creato in Datastore, chiama il metodo put() sull'oggetto note.

Recupero dei dati utente

Per recuperare i dati utente associati a un determinato ID utente, utilizza il metodo query() NDB per cercare nel database le note nello stesso gruppo di entità. Le entità nello stesso gruppo, o nel percorso predecessore, condividono un nome di chiave comune, in questo caso l'ID utente.

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

Puoi quindi recuperare i dati della query e visualizzare le note nel 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));
    });
  });
}

Deployment dell'app

Hai integrato correttamente Identity Platform con la tua applicazione App Engine. Per vedere la tua applicazione in esecuzione in un ambiente di produzione in funzione:

  1. Cambia l'URL dell'host di backend in main.js in https://backend-dot-[PROJECT_ID].appspot.com. Sostituisci [PROJECT_ID] con l'ID progetto.
  2. Esegui il deployment dell'applicazione utilizzando l'interfaccia a riga di comando di Google Cloud SDK:

    gcloud app deploy backend/index.yaml frontend/app.yaml backend/app.yaml
    
  3. Visualizza l'applicazione pubblicata all'indirizzo https://[PROJECT_ID].appspot.com.

Esegui la pulizia

Per evitare che al tuo account Google Cloud vengano addebitati costi relativi alle risorse utilizzate in questo tutorial, elimina il tuo progetto App Engine:

Elimina il progetto

Il modo più semplice per eliminare la fatturazione è eliminare il progetto che hai creato per il tutorial.

Per eliminare il progetto:

  1. Nella console Google Cloud, vai alla pagina Gestisci risorse.

    Vai a Gestisci risorse

  2. Nell'elenco dei progetti, seleziona il progetto che vuoi eliminare, quindi fai clic su Elimina.
  3. Nella finestra di dialogo, digita l'ID del progetto e fai clic su Chiudi per eliminare il progetto.

Passaggi successivi

  • Esplora le architetture di riferimento, i diagrammi e le best practice su Google Cloud. Visita il nostro Cloud Architecture Center.