Permitir que los usuarios accedan en App Engine


En este instructivo, se muestra cómo recuperar, verificar y almacenar credenciales de terceros mediante Identity Platform, el entorno estándar de App Engine, y Datastore.

En este documento, se proporcionan instrucciones respecto de una aplicación sencilla para tomar notas llamada Firenotes, que almacena las notas de los usuarios en sus propios notebooks. Los notebooks se almacenan por usuario y se identifican por el ID de Identity Platform único de cada usuario. La aplicación tiene los siguientes componentes:

  • El frontend configura la interfaz de usuario de acceso y recupera el ID de Identity Platform. También controla los cambios de estado de autenticación y permite a los usuarios ver sus notas.

  • FirebaseUI es una solución directa de código abierto que simplifica las tareas de IU y autenticación. El SDK controla el acceso del usuario mediante la vinculación de varios proveedores a una cuenta, la recuperación de contraseñas, etcétera. Implementa prácticas recomendadas de autenticación para una experiencia de acceso segura y sin problemas.

  • El backend verifica el estado de autenticación del usuario y muestra la información del perfil del usuario, así como sus notas.

La aplicación almacena las credenciales de los usuarios en Datastore mediante la biblioteca cliente de NDB, pero puedes almacenar las credenciales en la base de datos que quieras.

Firenotes se basa en el framework de aplicaciones web Flask. La app de muestra usa Flask porque es sencillo y fácil de usar, pero los conceptos y las tecnologías que se exploran se pueden aplicar sin importar el marco de trabajo que uses.

Objetivos

Una vez que completes este instructivo, habrás logrado lo siguiente:

  • Configurar la interfaz de usuario con FirebaseUI para Identity Platform
  • Obtener un token de ID de Identity Platform y verificarlo a través de la autenticación del servidor
  • Almacenar las credenciales de los usuarios y los datos asociados en Datastore
  • Consultar una base de datos mediante la biblioteca cliente de NDB
  • Implementar una aplicación en App Engine

Costos

En este instructivo, se usan componentes facturables de Google Cloud, que incluyen lo siguiente:

  • Datastore
  • Identity Platform

Usa la calculadora de precios para generar una estimación de los costos según el uso previsto. Es posible que los usuarios nuevos de Google Cloud califiquen para obtener una prueba gratuita.

Antes de comenzar

  1. Instala Git, Python 2.7 y virtualenv. Para obtener más información sobre cómo configurar tu entorno de desarrollo de Python, por ejemplo, a fin de instalar la última versión de Python, consulta Configura un entorno de desarrollo de Python para Google Cloud.
  2. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  3. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  4. Install the Google Cloud CLI.
  5. To initialize the gcloud CLI, run the following command:

    gcloud init
  6. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  7. Install the Google Cloud CLI.
  8. To initialize the gcloud CLI, run the following command:

    gcloud init

Si ya instalaste y, luego, inicializaste el SDK en un proyecto diferente, configura el proyecto de gcloud con el ID del proyecto de App Engine que usas para Firenotes. Consulta Administra parámetros de configuración del SDK de Google Cloud si deseas ver los comandos específicos para actualizar un proyecto con la herramienta de gcloud.

Clona la app de muestra

Para descargar la muestra a tu máquina local, haz lo siguiente:

  1. Clona el repositorio de la aplicación de muestra en tu máquina local:

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

    De manera opcional, puedes descargar la muestra como un archivo ZIP y extraerla.

  2. Navega hasta el directorio siguiente que contiene el código de muestra:

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

Agrega la interfaz de usuario

A fin de configurar FirebaseUI para Identity Platform y habilitar proveedores de identidad, haz lo siguiente:

  1. Para agregar Identity Platform a tu app, sigue estos pasos:

    1. Ve a la consola de Google Cloud.
      Ve a la consola de Google Cloud
    2. Selecciona el proyecto de Google Cloud que deseas usar:
      • Si tienes un proyecto existente, selecciónalo en la lista desplegable Seleccionar una organización en la parte superior de la página.
      • Si no tienes un proyecto de Google Cloud existente, crea uno nuevo en la consola de Google Cloud.
    3. Ve a la página Marketplace de Identity Platform en la consola de Google Cloud.
      Ir a la página Marketplace de Identity Platform
    4. En la página Marketplace de Identity Platform, haz clic en Enable Customer Identity.
    5. Ve a la página Usuarios de Customer Identity en la consola de Google Cloud.
      Ir a la página Usuarios
    6. En la parte superior derecha, haz clic en detalles de la configuración de la aplicación.
    7. Copia los detalles de la configuración de la aplicación en tu aplicación 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. Edita el archivo backend/app.yaml y, luego, ingresa el ID de tu proyecto de Google Cloud en las variables de entorno:

    # 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. En el archivo frontend/main.js, configura el widget de acceso de FirebaseUI mediante la selección de los proveedores que deseas ofrecer a tus usuarios.

    // 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. En la consola de Google Cloud, habilita los proveedores que elegiste conservar:

    1. Ve a la página Proveedores de Customer Identity en la consola de Google Cloud.
      Ir a la página Proveedores
    2. Haz clic en Agregar un proveedor.
    3. En la lista desplegable Seleccionar un proveedor, selecciona los proveedores que deseas usar.
    4. Junto a Habilitado, haz clic en el botón para habilitar el proveedor.
      • Para proveedores de identidad de terceros, ingresa el ID y el secreto del sitio del desarrollador del proveedor. Los documentos de Firebase proporcionan instrucciones específicas en las secciones “Antes de comenzar” de las guías de Facebook, Twitter y GitHub.
      • Para las integraciones de SAML y OIDC, consulta la configuración en tu IdP.
  5. Agrega tu dominio a la lista de dominios autorizados en Identity Platform:

    1. Ve a la página Configuración de Customer Identity en la consola de Google Cloud.
      Ir a la página Configuración
    2. En Authorized Domains, haga clic en Add Domain.
    3. Ingresa el dominio de tu app en el siguiente formato:

      [PROJECT_ID].appspot.com
      

      No incluyas http:// antes del nombre de dominio.

Instala dependencias

  1. Navega al directorio de backend y completa la configuración de la aplicación:

    cd backend/
    
  2. Instala las dependencias en un directorio lib en tu proyecto:

    pip install -t lib -r requirements.txt
    
  3. En appengine_config.py, el método vendor.add() registra las bibliotecas en el directorio lib.

Ejecuta la aplicación de manera local

Para ejecutar la aplicación de manera local, usa el servidor de desarrollo local de App Engine mediante los siguientes pasos:

  1. Agrega la siguiente URL como backendHostURL en main.js:

    http://localhost:8081

  2. Navega hasta el directorio raíz de la aplicación. Luego, inicia el servidor de desarrollo:

    dev_appserver.py frontend/app.yaml backend/app.yaml
    
  3. Visita http://localhost:8080/ en un navegador web.

Cómo autenticar usuarios en el servidor

Ahora que configuraste un proyecto y también inicializaste una aplicación para el desarrollo, puedes revisar el código a fin de comprender cómo recuperar y verificar los tokens del ID de Identity Platform en el servidor.

Obtén un token de ID de Identity Platform

El primer paso de la autenticación en el servidor es recuperar el token de acceso que se verificará. Las solicitudes de autenticación se manejan con el objeto de escucha onAuthStateChanged() de 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();

  }
});

Cuando un usuario accede, el método getToken() de Identity Platform en la devolución de llamada muestra un token de ID de Identity Platform en forma de token web JSON (JWT).

Verifica los tokens en el servidor

Después de que un usuario accede, el servicio de frontend recupera las notas existentes en el notebook del usuario a través de una solicitud GET de AJAX. Esto requiere autorización para acceder a los datos del usuario, por lo que el JWT se envía en el encabezado Authorization de la solicitud mediante el esquema 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
    }
  })

Antes de que el cliente pueda acceder a los datos del servidor, tu servidor debe verificar que Identity Platform firme el token. Puedes verificar este token a través de la biblioteca de autenticación de Google para Python. Usa la función verify_firebase_token de la biblioteca de autenticación para verificar el token del portador y extraer las reclamaciones:

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

Cada proveedor de identidad envía un conjunto diferente de reclamaciones, pero cada uno tiene al menos una sub reclamación con un ID de usuario único y una reclamación que proporciona información de perfil, como el name o email, que puedes usar para personalizar la experiencia del usuario en tu app.

Administra datos del usuario en Datastore

Una vez que autentiques a un usuario, debes almacenar sus datos para que perduren después de que finalice el acceso a una sesión. Las siguientes secciones explican cómo almacenar una nota como una entidad de Datastore y segregar las entidades por ID de usuario.

Crea entidades para almacenar datos del usuario

Puedes crear una entidad en Datastore si declaras una clase de modelo de NDB con ciertas propiedades, como números enteros o strings. Datastore indexa las entidades por similares y, en el caso de Firenotes, el similar de cada entidad es Note. Para fines de consulta, cada Note se almacena con un nombre de clave, que es el ID del usuario obtenido de la sub reclamación en la sección anterior.

El siguiente código muestra cómo establecer las propiedades de una entidad, con el método del constructor para la clase de modelo cuando se crea la entidad y a través de la asignación de propiedades individuales después de la creación:

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

Para escribir la Note recién creada en Datastore, realiza una llamada al método put() en el objeto note.

Recupera datos del usuario

Para recuperar los datos del usuario asociados con un ID del usuario en particular, usa el método query() de NDB para buscar notas en la base de datos en el mismo grupo de entidades. Las entidades en el mismo grupo o ruta de acceso principal comparten un nombre de clave común que, en este caso, es el ID del usuario.

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

Luego, puedes obtener los datos de la consulta y mostrar las notas en el cliente:

// 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));
    });
  });
}

Implementa la app

Integraste de manera adecuada Identity Platform con tu aplicación de App Engine. Para ver la aplicación en ejecución en un entorno de producción en vivo, haz lo siguiente:

  1. Cambia la URL del host de backend en main.js a https://backend-dot-[PROJECT_ID].appspot.com. Reemplaza [PROJECT_ID] por el ID del proyecto.
  2. Implementa la aplicación mediante la interfaz de línea de comandos del SDK de Google Cloud:

    gcloud app deploy backend/index.yaml frontend/app.yaml backend/app.yaml
    
  3. Visualiza la aplicación en vivo en https://[PROJECT_ID].appspot.com.

Limpia

Para evitar que se apliquen cargos a tu cuenta de Google Cloud por los recursos que usaste en este instructivo, borra tu proyecto de App Engine de la siguiente manera:

Borra el proyecto

La manera más fácil de eliminar la facturación es borrar el proyecto que creaste para el instructivo.

Para borrar el proyecto, sigue estos pasos:

  1. In the Google Cloud console, go to the Manage resources page.

    Go to Manage resources

  2. In the project list, select the project that you want to delete, and then click Delete.
  3. In the dialog, type the project ID, and then click Shut down to delete the project.

¿Qué sigue?

  • Explora arquitecturas de referencia, diagramas y prácticas recomendadas sobre Google Cloud. Consulta nuestro Cloud Architecture Center.