Saltar al contenido principal
Auth0 almacena la información de los usuarios de su inquilino en una base de datos alojada en la nube, o bien puede optar por almacenar los datos de usuario en su propia base de datos externa personalizada. Para almacenar datos de usuario además de la información básica que Auth0 utiliza para la autenticación, puede usar el almacén de datos de Auth0 o una base de datos personalizada. Sin embargo, si utiliza los datos adicionales con fines de autenticación, le recomendamos usar el almacén de datos de Auth0, ya que esto le permite administrar sus datos de usuario desde el Dashboard de administración de Auth0.

Base de datos externa frente al almacén de datos de Auth0

El almacén de datos de Auth0 está diseñado para datos de autenticación. Almacenar cualquier cosa más allá de la información predeterminada del usuario solo debería hacerse en casos limitados. A continuación se explica por qué:
  • Escalabilidad: El almacén de datos de Auth0 tiene limitaciones de escalabilidad, y los datos de su aplicación pueden superar los límites adecuados. Al usar una base de datos externa, mantiene simple su almacén de datos de Auth0, mientras que la base de datos externa, más eficiente, contiene los datos adicionales;
  • Rendimiento: Es probable que se acceda a sus datos de autenticación con menos frecuencia que a sus otros datos. El almacén de datos de Auth0 no está optimizado para un uso de alta frecuencia, por lo que debería almacenar en otro lugar los datos que deban recuperarse más a menudo;
  • Flexibilidad: Debido a que el almacén de datos de Auth0 se creó para admitir únicamente perfiles de usuario y sus metadatos asociados, sus posibilidades de actuar sobre la base de datos son limitadas. Al usar bases de datos independientes para sus otros datos, puede gestionarlos según corresponda y realizar llamadas directas sin usar la de Auth0.
Al externalizar la autenticación de usuarios, por lo general no es necesario mantener su propia tabla de usuarios/contraseñas. Aun así, es posible que quiera asociar los datos de la aplicación con usuarios autenticados.
  • Por ejemplo, podría tener una tabla Users que enumere cada usuario autenticado por Auth0. Cada vez que un usuario inicie sesión, podría buscar a ese usuario en la tabla. Si el usuario no existe, crearía un nuevo registro. Si sí existe, actualizaría todos los campos, manteniendo así una copia local de todos los datos del usuario.
  • Como alternativa, podría almacenar el identificador del usuario en cada tabla/colección que tenga datos asociados al usuario. Esta es una implementación más sencilla, adecuada para aplicaciones más pequeñas.

Escenario de ejemplo de almacenamiento de datos de usuario

Auth0 proporciona una aplicación de ejemplo —una aplicación móvil de música— que refleja la experiencia completa del usuario al usar Auth0 con una base de datos externa personalizada. La aplicación de ejemplo es una app para iOS creada con el proyecto semilla de Auth0 para iOS. El backend usa la API de Node.js. Para ver una visualización de la estructura general de la aplicación, consulte el escenario de arquitectura móvil + API.

Metadatos

Metadatos de la aplicación

Los siguientes datos de nuestra aplicación móvil de música son adecuados para almacenarse en app_metadata:
  • Plan de suscripción del usuario
  • Derecho del usuario (o su falta de autorización) para editar listas de reproducción destacadas
Estos dos datos deben almacenarse en app_metadata en lugar de user_metadata porque el usuario no debería poder modificarlos directamente.

Metadatos de usuario

Los siguientes datos de nuestra aplicación móvil de música son adecuados para almacenarse en user_metadata:
  • Preferencias de la aplicación
  • Opciones elegidas por el usuario para modificar su experiencia en la aplicación al iniciar sesión.
Ten en cuenta que, a diferencia de los datos de app_metadata, el usuario puede cambiar fácilmente los almacenados en user_metadata. Podemos permitir que el usuario cambie su displayName, que es el nombre que ve al iniciar sesión y que se muestra a otros usuarios de la aplicación. Para mostrar el identificador elegido por el usuario cada vez que inicia sesión, usamos una Rule para obtener el valor de user.user_metadata.
function(user, context, callback){
  user.user_metadata = user.user_metadata || {};
  user.user_metadata.displayName = user.user_metadata.displayName || "user";

  auth0.users.updateUserMetadata(user.user_id, user.user_metadata)
    .then(function(){
      callback(null, user, context);
    })
    .catch(function(err){
      callback(err);
    });
}
A continuación se muestra la pantalla que usaría el usuario para cambiar su displayName:
Pantalla de configuración de la app de iOS con la opción de actualizar el nombre para mostrar.
Para guardar los cambios en la base de datos, la aplicación realiza una llamada al endpoint Get a User de la Management API para identificar al usuario adecuado: A continuación, se hace una llamada al endpoint actualizar un usuario para actualizar el campo user_metadata: Sustituya {yourAccessToken} por un Token de acceso de Management API.

Reglas de permisos para datos de usuario

Use Rules para implementar permisos que determinen si un usuario puede editar listas de reproducción destacadas.

Asignar el rol Playlist Editor

La primera Rule envía una solicitud a nuestra API de Node, que luego consulta la base de datos conectada a Heroku para comprobar cuántas reproducciones tiene la lista de reproducción del usuario. Si el número es 100 o superior, asignamos playlist_editor como un valor del array roles en app_metadata.
function (user, context, callback) {

  var request = require('request');

  user.app_metadata = user.app_metadata || {};
  user.app_metadata.roles = user.roles || [];

  var CLIENT_SECRET = configuration.AUTH0_CLIENT_SECRET;
  var CLIENT_ID = configuration.AUTH0_CLIENT_ID;

  var scope = {
    user_id: user.user_id,
    email: user.email,
    name: user.name
  };

  var options = {
    subject: user.user_id,
    expiresInMinutes: 600,
    audience: CLIENT_ID,
    issuer: 'https://example.auth0.com'
  };

  var id_token = jwt.sign(scope, CLIENT_SECRET, options);

  var auth = 'Bearer ' + id_token;

  request.get({
    url: 'https://example.com/playlists/getPlays',
    headers: {
       'Authorization': auth,
      'Content-Type': 'text/html'
    },
    timeout: 15000
  }, function(err, response, body){
    if (err)
      return callback(new Error(err));
    var plays = parseInt(body, 10);

    if (plays >= 100 && user.roles.indexOf('playlist_editor') < 0){
      user.app_metadata.roles.push('playlist_editor');
      auth0.users.updateAppMetadata(user.user_id, user.app_metadata)
        .then(function(){
          callback(null, user, context);
        })
        .catch(callback);
    }

    else if (plays < 100 && user.roles.indexOf('playlist_editor') >= 0){
      user.app_metadata.roles = [];
      auth0.users.updateAppMetadata(user.user_id, user.app_metadata)
        .then(function(){
          callback(null, user, context);
        })
        .catch(callback);
    }
    else{
      callback(null, user, context);
    }

  });

}

El parámetro scope especifica roles

La segunda Rule obtiene el campo app_metadata y asigna el arreglo roles a un campo del objeto del usuario para que se pueda acceder a él sin necesidad de llamar a app_metadata desde la aplicación. El parámetro scope puede entonces especificar roles cuando el usuario inicia sesión, sin incluir todo el contenido de app_metadata en el objeto del usuario:
function(user, context, callback) {
   if (user.app_metadata) {
      user.roles = user.app_metadata.roles;
   }
   user.roles = user.roles || [];
   callback(null, user, context);
}
Después de implementar estas dos Rules, la aplicación reconoce si el usuario es editor de listas de reproducción o no, y cambia la pantalla de bienvenida en consecuencia. Si playlist_editor está en el array roles almacenado en app_metadata del usuario, se le dará la bienvenida como EDITOR después de iniciar sesión:
Ejemplo de la página de perfil de usuario con el rol de editor.

Asociar la música de un usuario a ese usuario

Necesitamos asociar la música de un usuario a ese usuario, pero esta información no es necesaria para la autenticación. A continuación, se muestra cómo almacenar esta información en una base de datos independiente integrada con el backend de la aplicación. El identificador único del usuario es user_id y es la subpropiedad sub del objeto idTokenPayload en un authResult. Esta es una fila de ejemplo de la tabla songs de nuestra base de datos:
song_idsongnameuser_id
1Number One Hitgoogle-oauth2
El backend de Node.js autentica las solicitudes al URI asociado con la obtención de los datos personales del usuario desde la base de datos mediante la validación de un . Obtén más información sobre la autenticación basada en tokens y cómo implementar JWT en tus aplicaciones. Aquí tienes el código que implementa la validación de JWT del proyecto semilla de Node.js:
var genres = require('./routes/genres');
var songs = require('./routes/songs');
var playlists = require('./routes/playlists');
var displayName = require('./routes/displayName');

var authenticate = jwt({
  secret: process.env.AUTH0_CLIENT_SECRET,
  audience: process.env.AUTH0_CLIENT_ID
});

app.use('/genres', authenticate, genres);
app.use('/songs', authenticate, songs);
app.use('/playlists', authenticate, playlists);
app.use('/displayName', authenticate, displayName);
Podemos añadir funcionalidad para gestionar distintas solicitudes de datos de nuestra aplicación. Por ejemplo, si recibimos una solicitud GET a /secured/getFavGenre, la API llama a la función queryGenre(), que consulta la base de datos y responde con el género favorito del usuario.
@IBAction func getGenre(sender: AnyObject) {
        let request = buildAPIRequest("/genres/getFav", type:"GET")
        let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {[unowned self](data, response, error) in
            let genre = NSString(data: data!, encoding: NSUTF8StringEncoding)
            dispatch_async(dispatch_get_main_queue(), {
                self.favGenre.text = "Favorite Genre:  \(genre!)"
            })
        }
        task.resume()
    }
La función buildAPIRequest() toma la ruta y el método HTTP de la solicitud como parámetros y crea una solicitud con la URL base de nuestra API de Node.js alojada en Heroku. En la aplicación, la función getGenre() realiza una solicitud a la API y cambia la interfaz para mostrar la respuesta de la solicitud a /genres/getFav. El backend recupera los datos necesarios para esta acción mediante la función queryGenre() y devuelve los resultados a la aplicación:
function queryGenre(user_id, res){

  db.connect(process.env.DATABASE_URL, function(err, client) {
  if (err) throw err;

  client
    .query('SELECT fav_genre as value FROM user_data WHERE user_id = $1', [user_id], function(err, result) {

      if(err) {
        return console.error('error running query', err);
      }
      res.send(result.rows[0].value);
    });
  });

};

Más información