ユーザーデータの保存
Auth0ではテナントのユーザー情報をホストされたクラウドデータベースに保存しますが、独自の外部カスタムデータベースにユーザーデータを保存することもできます。
Auth0が認証に使用する基本情報以外のユーザーデータを保存するには、Auth0のデータストアまたはカスタムデータベースを使用できます。ただし、その追加データを認証目的で使用する場合は、ユーザーデータをAuth0 Management Dashboardで管理できるよう、Auth0のデータストアを使用することを推奨します。
外部データベースとAuth0データストア
Auth0データストアは認証データ用にカスタマイズされています。デフォルトのユーザー情報以外のデータ保存は、以下の理由により、限られた場合にのみ行ってください。
スケーラビリティ:Auth0データストアの拡張性は限られており、アプリケーションのデータが適切な限度を超える可能性があります。外部データベースを使用することにより、Auth0データストアをシンプルに保ちつつ、より効率的な外部データベースに追加データを格納することができます。
パフォーマンス:認証データへのアクセス頻度は、他のデータよりも低い傾向があります。Auth0データストアは高頻度の使用には最適化されていないため、より頻繁に取得されるデータは別の場所に保存する必要があります。
フレキシビリティ:Auth0データストアはユーザープロファイルと関連するメタデータのみを格納するように構築されているので、データベースで実行できるアクションが制限されています。そのため、他のデータには異なるデータベースを使用することで、データを適切に管理することができます。
ユーザー認証をアウトソーシングする場合、通常は独自のユーザー/パスワードテーブルを維持する必要はありません。しかし、アプリケーションデータを認証されたユーザーに関連付けた方が良い場合があります。
たとえば、Auth0によって認証された各ユーザーを一覧にしたユーザーテーブルを作成することができます。ユーザーがログインするたびに、このテーブルで該当するユーザーを検索します。ユーザーが存在しない場合には、新しいレコードを作成します。存在する場合には、すべてのフィールドを更新して、基本的にすべてのユーザーデータのローカルコピーを保存します。
もしくは代わりに、ユーザーに関連付けられたデータのある各テーブル/コレクションにユーザー識別子を保存することもできます。これは、小さなアプリケーションに適したシンプルな方法です。
ユーザーデータ保存のシナリオ例
Auth0ではサンプルアプリとしてモバイル音楽アプリケーションを提供しており、Auth0と外部カスタムデータベースを使用したときのエンドツーエンドのユーザーエクスペリエンスを確認できます。このサンプルアプリはAuth0 iOSのシードプロジェクトを活用して作成されたiOSアプリです。バックエンドはNode.js APIを使用しています。
このアプリケーションの全体的な構造のビジュアライゼーションについては、モバイル+APIアーキテクチャシナリオを参照してください。
メタデータ
アプリメタデータ
モバイル音楽アプリケーションの以下のデータポイントを保存する場所としては、app_metadata
が適しています。
ユーザーのサブスクリプションプラン
おすすめのプレイリストを編集するユーザーの権限の有無
この2つのデータポイントは、ユーザーが直接変更できてはならないため、user_metadata
ではなくapp_metadata
に保存しなければなりません。
ユーザーメタデータ
モバイル音楽アプリケーションの以下のデータポイントを保存する場所としては、user_metadata
が適しています。
アプリケーションの環境設定
ログイン時のアプリのエクスペリエンスを変更するためにユーザーが選択した詳細
app_metadata
のデータポイントとは異なり、user_metadata
に保存されたこれらの情報はユーザーが簡単に変更できます。
ユーザーがログイン時に目にし、アプリの他のユーザーに表示されるユーザー名、displayName
をユーザーが変更できるようにすることもできます。
ユーザーが選択した識別子をログイン時に表示するには、ルールを使用して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?
ユーザーが自身のdisplayName
を変更するために使用する画面はこのようになります。

変更内容をデータベースに保存するため、アプリケーションがManagement APIのユーザー取得エンドポイントを呼び出して適切なユーザーを識別します。
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?
その後、ユーザー更新エンドポイントが呼び出され、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?
{yourAccessToken}
をManagement APIアクセストークンに置き換える必要があります。
ユーザーデータの権限ルール
ルールを使用して、ユーザーがおすすめのプレイリストを編集できるかどうかを決める権限を実装します。
プレイリスト編集者ロールを割り当てる
1つめのルールは、Node APIに要求を送信し、Node APIはHerokuに接続されたデータベースにクエリを実行して、ユーザーのプレイリストの再生回数を確認します。回数が100以上の場合は、app_metadata
のroles
配列の値としてplaylist_editor
が割り当てられます。
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?
スコープパラメーターがロールを指定する
2つめのルールは、app_metadata
フィールドを取得して、ユーザーオブジェクトのフィールドにroles
配列を割り当てます。これにより、アプリケーションでapp_metadata
を呼び出すことなくアクセスできるようになります。これでscope
パラメーターは、ユーザーオブジェクトのapp_metadata
のすべてを含めることなく、ユーザーのログイン時にroles
を指定できます。
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?
この2つのルールを実装すると、アプリはユーザーがプレイリスト編集者かどうかを判断でき、ロールに応じてウェルカム画面を変更します。ユーザーのapp_metadata
に保存されているroles
配列にplaylist_editor
がある場合、ユーザーにはサインイン後にEDITOR(編集者)向けの画面が表示されます。

ユーザーの音楽をユーザーに関連付ける
このアプリでは、ユーザーの音楽をユーザーに関連付ける必要がありますが、この情報は認証には必要ありません。この非認証情報をアプリケーションのバックエンドと統合された個別のデータベースに保存する方法を説明します。
ユーザーの一意の識別子はuser_id
です。データベースのsongs
テーブルにあるサンプル行がこちらです。
song_id | songname | user_id |
---|---|---|
1 | No.1ヒットソング | google-oauth2 |
Node.jsのバックエンドが、JSON Web Tokenを検証して、データベースからのユーザーの個人データ取得に関連付けられたURIへの要求を認証します。
トークンベースの認証とアプリケーションにJWTを実装する方法
こちらは、Node.jsシードプロジェクトからの、JWT検証を実装するコードです。
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?
アプリケーションからのさまざまなデータ要求を処理する機能性を追加できます。たとえば、/secured/getFavGenre
へのGET
要求を受信したら、APIがqueryGenre()
関数を呼び出し、データベースを照会してユーザーの好きなジャンルで応答します。
@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?
buildAPIRequest()
関数は、要求のパスとHTTPメソッドをパラメーターとして受け取り、HerokuでホストされているNode.js APIのベースURLを使用して要求を構築します。
アプリケーションでは、getGenre()
関数がAPIに要求を送信し、アプリのインターフェイスを変更して/genres/getFav
への要求の応答を表示します。バックエンドはこのアクションに必要なデータをqueryGenre()
関数を使用して取得し、結果をアプリケーションに返します。
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?