Stockage de données utilisateur

Auth0 stocke les informations des utilisateurs de votre locataire dans une base de données hébergée dans le nuage, ou vous pouvez choisir de stocker les données des utilisateurs dans votre propre base de données externe personnalisée.

Pour stocker des données utilisateur au-delà des informations de base qu’Auth0 utilise pour l’authentification, vous pouvez utiliser le magasin de données Auth0 ou une base de données personnalisée. Cependant, si vous utilisez les données supplémentaires à des fins d’authentification, nous vous recommandons d’utiliser le magasin de données Auth0, car il vous permet de gérer vos données utilisateur via le Auth0 Management Dashboard.

Base de données externe par rapport au magasin de données Auth0

Le magasin de données Auth0 est personnalisé pour les données d’authentification. Le stockage d’informations autres que celles de l’utilisateur par défaut ne doit être effectué que dans des cas limités. Voici pourquoi :

  • Évolutivité : Le magasin de données Auth0 est limité en termes d’évolutivité, et les données de votre application peuvent dépasser les limites appropriées. En utilisant une base de données externe, vous gardez votre magasin de données Auth0 simplifié, tandis que la base de données externe, plus efficace, contient les données supplémentaires;

  • Performance : Vos données d’authentification sont probablement consultées moins souvent que vos autres données. Le magasin de données Auth0 n’est pas optimisé pour une utilisation à haute fréquence, c’est pourquoi vous devriez stocker ailleurs les données qui doivent être récupérées plus souvent;

  • Flexibilité : Le magasin de données Auth0 ayant été conçu pour accueillir uniquement des profils utilisateurs et leurs métadonnées, les actions que vous pouvez effectuer sur la base de données sont limitées. En utilisant des bases de données séparées pour vos autres données, vous pouvez gérer vos données de manière appropriée.

Lorsque vous externalisez l’authentification des utilisateurs, il n’est généralement pas nécessaire de maintenir votre propre table utilisateurs/mots de passe. Néanmoins, il se peut que vous souhaitiez associer des données d’application à des utilisateurs authentifiés.

  • Par exemple, vous pourriez avoir une table Utilisateurs qui répertorie chaque utilisateur authentifié par Auth0. Chaque fois qu’un utilisateur se connecte, vous pouvez rechercher cet utilisateur dans la table. Si l’utilisateur n’existe pas, vous créez un nouvel enregistrement. S’il existe, vous mettez à jour tous les champs, conservant ainsi une copie locale de toutes les données de l’utilisateur.

  • Vous pouvez également stocker l’identifiant de l’utilisateur dans chaque table/collection contenant des données associées à l’utilisateur. Il s’agit d’une implémentation plus simple, adaptée aux petites applications.

Exemple de scénario de stockage de données utilisateur

Auth0 fournit un exemple d’application, une application de musique mobile, qui reflète l’expérience utilisateur de bout en bout lors de l’utilisation d’Auth0 avec une base de données externe personnalisée. Cet exemple d’application est une application iOS créée à l’aide du projet seed Auth0 iOS. Le système dorsal utilise l’API Node.js.

Pour une visualisation de la structure globale de l’application, voir le scénario d’architecture Mobile + API.

Métadonnées

Métadonnées d’application

Les points de données suivants de notre application de musique mobile sont appropriés pour être stockés dans app_metadata :

  • Plan d’abonnement de l’utilisateur

  • Droit (ou absence de droit) de l’utilisateur à modifier les listes de lecture en vedette

Ces deux points de données doivent être stockés dans app_metadata plutôt que dans user_metadata, car ils ne doivent pas être directement modifiables par l’utilisateur.

Métadonnées de l’utilisateur

Les points de données suivants, issus de notre application de musique mobile, peuvent être stockés dans user_metadata :

  • Préférences de l’application

  • Détails choisis par l’utilisateur pour modifier son expérience de l’application lors de la connexion.

Notez que, contrairement aux points de données pour app_metadata, l’utilisateur peut facilement et aisément modifier ceux stockés dans user_metadata.

Nous pouvons permettre à l’utilisateur de modifier son displayName, qui est le nom que l’utilisateur voit lorsqu’il se connecte et qui est affiché aux autres utilisateurs de l’application.

Pour afficher l’identifiant choisi par l’utilisateur à chaque fois qu’il se connecte, nous utilisons une règle pour obtenir la valeur 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);
    });
}

Was this helpful?

/

Voici un aperçu de l’écran que l’utilisateur utiliserait pour modifier son displayName :

Écran des paramètres de l'application iOS avec option de mise à jour du nom d'affichage.

pour enregistrer les modifications dans la base de données, l’application appelle le point de terminaison Obtenir un utilisateur de Management API pour identifier l’utilisateur approprié :


curl --request GET \
  --url https://%7ByourAccount%7D.auth0.com/api/v2/users/user_id \
  --header 'authorization: Bearer {yourIdToken}'

Was this helpful?

/
var client = new RestClient("https://%7ByourAccount%7D.auth0.com/api/v2/users/user_id");
var request = new RestRequest(Method.GET);
request.AddHeader("authorization", "Bearer {yourIdToken}");
IRestResponse response = client.Execute(request);

Was this helpful?

/
package main

import (
	"fmt"
	"net/http"
	"io/ioutil"
)

func main() {

	url := "https://%7ByourAccount%7D.auth0.com/api/v2/users/user_id"

	req, _ := http.NewRequest("GET", url, nil)

	req.Header.Add("authorization", "Bearer {yourIdToken}")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := ioutil.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}

Was this helpful?

/
HttpResponse<String> response = Unirest.get("https://%7ByourAccount%7D.auth0.com/api/v2/users/user_id")
  .header("authorization", "Bearer {yourIdToken}")
  .asString();

Was this helpful?

/
var axios = require("axios").default;

var options = {
  method: 'GET',
  url: 'https://%7ByourAccount%7D.auth0.com/api/v2/users/user_id',
  headers: {authorization: 'Bearer {yourIdToken}'}
};

axios.request(options).then(function (response) {
  console.log(response.data);
}).catch(function (error) {
  console.error(error);
});

Was this helpful?

/
#import <Foundation/Foundation.h>

NSDictionary *headers = @{ @"authorization": @"Bearer {yourIdToken}" };

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://%7ByourAccount%7D.auth0.com/api/v2/users/user_id"]
                                                       cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                   timeoutInterval:10.0];
[request setHTTPMethod:@"GET"];
[request setAllHTTPHeaderFields:headers];

NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
                                            completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
                                                if (error) {
                                                    NSLog(@"%@", error);
                                                } else {
                                                    NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
                                                    NSLog(@"%@", httpResponse);
                                                }
                                            }];
[dataTask resume];

Was this helpful?

/
$curl = curl_init();

curl_setopt_array($curl, [
  CURLOPT_URL => "https://%7ByourAccount%7D.auth0.com/api/v2/users/user_id",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "GET",
  CURLOPT_HTTPHEADER => [
    "authorization: Bearer {yourIdToken}"
  ],
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;
}

Was this helpful?

/
import http.client

conn = http.client.HTTPSConnection("")

headers = { 'authorization': "Bearer {yourIdToken}" }

conn.request("GET", "%7ByourAccount%7D.auth0.com/api/v2/users/user_id", headers=headers)

res = conn.getresponse()
data = res.read()

print(data.decode("utf-8"))

Was this helpful?

/
require 'uri'
require 'net/http'
require 'openssl'

url = URI("https://%7ByourAccount%7D.auth0.com/api/v2/users/user_id")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE

request = Net::HTTP::Get.new(url)
request["authorization"] = 'Bearer {yourIdToken}'

response = http.request(request)
puts response.read_body

Was this helpful?

/
import Foundation

let headers = ["authorization": "Bearer {yourIdToken}"]

let request = NSMutableURLRequest(url: NSURL(string: "https://%7ByourAccount%7D.auth0.com/api/v2/users/user_id")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()

Was this helpful?

/

cet appel est suivi d’un appel au point de terminaison Mettre à jour un utilisateur pour mettre à jour le champ user_metadata :


curl --request PATCH \
  --url 'https://{yourDomain}/api/v2/users/user_id' \
  --header 'authorization: Bearer {yourAccessToken}' \
  --header 'content-type: application/json' \
  --data '{"user_metadata": {"displayName": "J-vald3z"}'

Was this helpful?

/
var client = new RestClient("https://{yourDomain}/api/v2/users/user_id");
var request = new RestRequest(Method.PATCH);
request.AddHeader("authorization", "Bearer {yourAccessToken}");
request.AddHeader("content-type", "application/json");
request.AddParameter("application/json", "{\"user_metadata\": {\"displayName\": \"J-vald3z\"}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);

Was this helpful?

/
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io/ioutil"
)

func main() {

	url := "https://{yourDomain}/api/v2/users/user_id"

	payload := strings.NewReader("{\"user_metadata\": {\"displayName\": \"J-vald3z\"}")

	req, _ := http.NewRequest("PATCH", url, payload)

	req.Header.Add("authorization", "Bearer {yourAccessToken}")
	req.Header.Add("content-type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := ioutil.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}

Was this helpful?

/
HttpResponse<String> response = Unirest.patch("https://{yourDomain}/api/v2/users/user_id")
  .header("authorization", "Bearer {yourAccessToken}")
  .header("content-type", "application/json")
  .body("{\"user_metadata\": {\"displayName\": \"J-vald3z\"}")
  .asString();

Was this helpful?

/
var axios = require("axios").default;

var options = {
  method: 'PATCH',
  url: 'https://{yourDomain}/api/v2/users/user_id',
  headers: {authorization: 'Bearer {yourAccessToken}', 'content-type': 'application/json'},
  data: '{"user_metadata": {"displayName": "J-vald3z"}'
};

axios.request(options).then(function (response) {
  console.log(response.data);
}).catch(function (error) {
  console.error(error);
});

Was this helpful?

/
#import <Foundation/Foundation.h>

NSDictionary *headers = @{ @"authorization": @"Bearer {yourAccessToken}",
                           @"content-type": @"application/json" };

NSData *postData = [[NSData alloc] initWithData:[@"{"user_metadata": {"displayName": "J-vald3z"}" dataUsingEncoding:NSUTF8StringEncoding]];

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://{yourDomain}/api/v2/users/user_id"]
                                                       cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                   timeoutInterval:10.0];
[request setHTTPMethod:@"PATCH"];
[request setAllHTTPHeaderFields:headers];
[request setHTTPBody:postData];

NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
                                            completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
                                                if (error) {
                                                    NSLog(@"%@", error);
                                                } else {
                                                    NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
                                                    NSLog(@"%@", httpResponse);
                                                }
                                            }];
[dataTask resume];

Was this helpful?

/
$curl = curl_init();

curl_setopt_array($curl, [
  CURLOPT_URL => "https://{yourDomain}/api/v2/users/user_id",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "PATCH",
  CURLOPT_POSTFIELDS => "{\"user_metadata\": {\"displayName\": \"J-vald3z\"}",
  CURLOPT_HTTPHEADER => [
    "authorization: Bearer {yourAccessToken}",
    "content-type: application/json"
  ],
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;
}

Was this helpful?

/
import http.client

conn = http.client.HTTPSConnection("")

payload = "{\"user_metadata\": {\"displayName\": \"J-vald3z\"}"

headers = {
    'authorization': "Bearer {yourAccessToken}",
    'content-type': "application/json"
    }

conn.request("PATCH", "/{yourDomain}/api/v2/users/user_id", payload, headers)

res = conn.getresponse()
data = res.read()

print(data.decode("utf-8"))

Was this helpful?

/
require 'uri'
require 'net/http'
require 'openssl'

url = URI("https://{yourDomain}/api/v2/users/user_id")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE

request = Net::HTTP::Patch.new(url)
request["authorization"] = 'Bearer {yourAccessToken}'
request["content-type"] = 'application/json'
request.body = "{\"user_metadata\": {\"displayName\": \"J-vald3z\"}"

response = http.request(request)
puts response.read_body

Was this helpful?

/
import Foundation

let headers = [
  "authorization": "Bearer {yourAccessToken}",
  "content-type": "application/json"
]

let postData = NSData(data: "{"user_metadata": {"displayName": "J-vald3z"}".data(using: String.Encoding.utf8)!)

let request = NSMutableURLRequest(url: NSURL(string: "https://{yourDomain}/api/v2/users/user_id")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "PATCH"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()

Was this helpful?

/

vous devez remplacer {yourAccessToken} par un jeton d’accès à Management API.

Règles d’autorisation des données utilisateur

Utilisez des rules pour mettre en place des autorisations permettant à un utilisateur de modifier ou non les listes de lecture en vedette.

Attribuer le rôle d’éditeur de liste de lecture

La première règle envoie une demande à notre API Node, qui interroge ensuite la base de données connectée à Heroku pour vérifier le nombre de lectures de la liste de lecture de l’utilisateur. Si le nombre est égal ou supérieur à 100, nous attribuons la valeur playlist_editor au tableau roles dans 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);
    }

  });

}

Was this helpful?

/

Le paramètre scope spécifie le rôle

La deuxième règle récupère le champ app_metadata et affecte le tableau des roles à un champ de l’objet utilisateur afin qu’il soit accessible sans appeler app_metadata sur l’application. Le paramètre scope peut alors spécifier les roles lors de la connexion de l’utilisateur sans inclure tout ce qui se trouve dans app_metadata dans l’objet utilisateur :

function(user, context, callback) {
   if (user.app_metadata) {
      user.roles = user.app_metadata.roles;
   }
   user.roles = user.roles || [];
   callback(null, user, context);
}

Was this helpful?

/

Après avoir implémenté ces deux règles, l’application reconnaît si l’utilisateur est un éditeur de liste de lecture ou non et modifie l’écran d’accueil en conséquence. Si playlist_editor figure dans le tableau des roles stocké dans les app_metadata de l’utilisateur, ce dernier sera accueilli en tant que ÉDITEUR après s’être connecté :

Example of user profile page with editor role.

Associer la musique d’un utilisateur à l’utilisateur

Nous devons associer la musique d’un utilisateur à cet utilisateur, mais cette information n’est pas nécessaire pour l’authentification. Voici comment stocker ces informations dans une base de données distincte, intégrée au système dorsal de l’application.

L’identifiant unique de l’utilisateur est le user_id. Voici un exemple de ligne de la table songs de notre base de données :

song_id songname user_id
1 Succès numéro un google-oauth2

Le système dorsal Node.js authentifie les demandes adressées à l’URI associé à l’obtention des données personnelles de l’utilisateur à partir de la base de données en validant un jeton Web JSON.

En savoir plus sur l’authentification basée sur un jeton et comment mettre en œuvre le JWT dans vos applications.

Voici le code mettant en œuvre la validation JWT à partir du projet Node.js seed :

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

Was this helpful?

/

Nous pouvons ajouter des fonctionnalités pour gérer différentes demandes de données à partir de notre application. Par exemple, si nous recevons une demande GET à /secured/getFavGenre, l’API appelle la fonction queryGenre(), qui interroge la base de données et répond avec le genre préféré de l’utilisateur.

@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()
    }

Was this helpful?

/

La fonction buildAPIRequest() prend le chemin et la méthode HTTP de la demande comme paramètres et crée une demande en utilisant la base URL de notre API Node.js qui est hébergée sur Heroku.

Dans l’application, la fonction getGenre() fait une demande à l’API et modifie l’interface de l’application pour afficher la réponse à la demande /genres/getFav. Le système dorsal récupère les données nécessaires à cette action en utilisant la fonction queryGenre() et renvoie les résultats à l’application.

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

};

Was this helpful?

/

En savoir plus