Autentica usuarios en App Engine con Firebase


En este instructivo, se muestra cómo recuperar, verificar y almacenar las credenciales de los usuarios con Firebase Authentication, el entorno estándar de App Engine y Datastore.

Este documento proporciona instrucciones respecto de una aplicación sencilla para tomar notas, llamada Firenotes, que almacena las notas de los usuarios en sus propias notebooks. Los notebooks se almacenan por usuario y se identifican por el ID único de Firebase Authentication de cada usuario. La aplicación tiene los siguientes componentes:

  • El frontend configura la interfaz de usuario de acceso y recupera el ID de Firebase Authentication. 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 recomendaciones de autenticación para una experiencia de acceso sin problemas y segura.

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

En el siguiente diagrama, se muestra cómo el frontend y el backend se comunican entre sí y cómo las credenciales de los usuarios viajan desde Firebase hasta la base de datos.
Ruta de acceso de la solicitud con credenciales de usuario

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 de Firebase Authentication
  • Obtener un token de ID de Firebase y verificarlo a través de la autenticación en el 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

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. Accede a tu cuenta de Google Cloud. Si eres nuevo en Google Cloud, crea una cuenta para evaluar el rendimiento de nuestros productos en situaciones reales. Los clientes nuevos también obtienen $300 en créditos gratuitos para ejecutar, probar y, además, implementar cargas de trabajo.
  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
    
    Para configurar FirebaseUI y habilitar los proveedores de identidad, haz lo siguiente:

  3. Agrega Firebase a tu aplicación mediante estos pasos:

    1. Crea un proyecto en Firebase console.
      • Si no tienes un proyecto de Firebase existente, haz clic en Agregar proyecto e ingresa un nombre de proyecto existente de Google Cloud o uno nuevo.
      • Si tienes un proyecto de Firebase existente que desees usar, selecciónalo en la consola.
    2. En la página de descripción general del proyecto, haz clic en Agregar Firebase a tu aplicación web. Si tu proyecto ya tiene una app, selecciona Agregar app en la página de descripción general del proyecto.
    3. Usa la sección Initialize Firebase del fragmento de código personalizado de tu proyecto para completar la siguiente sección del archivo 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>"
      };
  4. 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);
    }
  5. Para habilitar los proveedores que decidiste conservar en Firebase console, haz clic en Authentication (Autenticación) > Sign-in method (Método acceso). Luego, en Proveedores de acceso, desplaza el cursor sobre un proveedor y haz clic en el ícono de lápiz.

    Proveedores de acceso

    1. Activa o desactiva el botón Habilitar y, para proveedores de identidad de terceros, ingresa el ID y el secreto del proveedor 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. Después de habilitar un proveedor, haz clic en Guardar.

      Habilita o inhabilita el botón “Activar”

    2. En Firebase console, en Dominios autorizados, haz clic en Agregar dominio y, luego, ingresa el dominio de tu aplicación en App Engine con 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 Firebase en el servidor.

Cómo obtener un token de ID de Firebase

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

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 accedió, el método getToken() de Firebase en la devolución de llamada muestra un token de ID de Firebase 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 Firebase firma 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 tu aplicación

Integraste correctamente Firebase Authentication a 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. En la consola de Google Cloud, ve a la página Administrar recursos.

    Ir a Administrar recursos

  2. En la lista de proyectos, elige el proyecto que quieres borrar y haz clic en Borrar.
  3. En el diálogo, escribe el ID del proyecto y, luego, haz clic en Cerrar para borrar el proyecto.

¿Qué sigue?

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