Auth0のRulesで使えるオブジェクトを出力してみた!
前提
今回はAuth0のRules
という機能に関しての記事です。Rules
自体知らない方は、公式ドキュメントのRulesを読むと幸せになれます。特にWhat can I use Rules for?の箇所にはRules
のユースケースが8つ提示されており、認証周りでシステムにありがちな要件にうまく対応出来る機能なんだな!という印象を受けました。
使えるオブジェクトとは?
Rules
は、「IDプロバイダーでの認証〜クライアントアプリにトークンが渡されるまで」の処理を、開発者が自由にコード定義する機能です。
コードは、Auth0のサンドボックス内で実行されるため、その環境内で様々なオブジェクト(変数)を利用する事ができます。
今回はそれらを実際に出力してみました。
出力時の構成
出力時の構成と注意点
- クライアントアプリ
Auth0のSINGLE PAGE APPLICATIONのAngularサンプルを利用 - ログインしたユーザー情報
サインアップ済み、user_metadata以外は未登録。ほぼデフォルト状態 - 注意点
出力結果はセキュリティや文字列の長さの関係上、xや...で省略している部分があります
出力に利用したソースコード
オブジェクトで返却される値は、適宜JSON.stringify()しています
function (user, context, callback) { console.log(`=========== user ===========`); console.log(`user.app_metadata: ${JSON.stringify(user.app_metadata)}`); console.log(`user.created_at: ${user.created_at}`); console.log(`user.email: ${user.email}`); console.log(`user.email_verified: ${user.email_verified}`); console.log(`user.family_name: ${user.family_name}`); console.log(`user.given_name: ${user.given_name}`); console.log(`user.identities: ${JSON.stringify(user.identities)}`); console.log(`user.last_password_reset: ${user.last_password_reset}`); console.log(`user.multifactor: ${user.multifactor}`); console.log(`user.name: ${user.name}`); console.log(`user.nickname: ${user.nickname}`); console.log(`user.phone_number: ${user.phone_number}`); console.log(`user.phone_verified: ${user.phone_verified}`); console.log(`user.picture: ${user.picture}`); console.log(`user.updated_at: ${user.updated_at}`); console.log(`user.user_id: ${user.user_id}`); console.log(`user.user_metadata: ${JSON.stringify(user.user_metadata)}`); console.log(`user.username: ${user.username}`); console.log(`=========== context ===========`); console.log(`context.tenant: ${context.tenant}`); console.log(`context.clientID: ${context.clientID}`); console.log(`context.clientName: ${context.clientName}`); console.log(`context.clientMetadata: ${JSON.stringify(context.clientMetadata)}`); console.log(`context.connectionID: ${context.connectionID}`); console.log(`context.connection: ${context.connection}`); console.log(`context.connectionStrategy: ${context.connectionStrategy}`); console.log(`context.connectionOptions: ${JSON.stringify(context.connectionOptions)}`); console.log(`context.connectionMetadata: ${JSON.stringify(context.connectionMetadata)}`); console.log(`context.samlConfiguration: ${JSON.stringify(context.samlConfiguration)}`); console.log(`context.protocol: ${context.protocol}`); console.log(`context.stats: ${JSON.stringify(context.stats)}`); console.log(`context.sso: ${JSON.stringify(context.sso)}`); console.log(`context.accessToken: ${JSON.stringify(context.accessToken)}`); console.log(`context.idToken: ${JSON.stringify(context.idToken)}`); console.log(`context.original_protocol: ${context.original_protocol}`); console.log(`context.multifactor: ${context.multifactor}`); console.log(`context.redirect: ${context.redirect}`); console.log(`context.sessionID: ${context.sessionID}`); console.log(`context.request: ${JSON.stringify(context.request)}`); console.log(`context.primaryUser: ${context.primaryUser}`); console.log(`context.authentication: ${JSON.stringify(context.authentication)}`); console.log(`context.authorization: ${JSON.stringify(context.authorization)}`); console.log(`=========== auth0 ===========`); console.log(`auth0: ${auth0}`); console.log(`=========== Configuration ===========`); console.log(`configuration: ${JSON.stringify(configuration)}`); callback(null, user, context); }
各オブジェクトの詳細
関数の引数に渡されるオブジェクト
user
各項目の説明やデータ型は、User Object in Rulesを参照してください。
項目名 | 出力結果 |
user.app_metadata | undefined |
user.created_at | 2019-08-23T00:43:48.837Z |
user.email | [email protected] |
user.email_verified | true |
user.given_name | undefined |
user.identities | [{"user_id":"5d5...","provider":"auth0","connection":"Username-Password-Authentication","isSocial":false}] |
user.last_password_reset | 2019-08-23T01:04:24.332Z |
user.multifactor | undefined |
user.name | [email protected] |
user.nickname | xxxxxxxx |
user.phone_number | undefined |
user.phone_verified | undefined |
user.picture | https://s.gravatar.com/ava..... |
user.updated_at | 2019-08-24T14:10:20.494Z |
user.user_id | auth0|5d5... |
user.user_metadata | {"test":true} |
user.username | undefined |
ほぼデフォルトなので、未設定箇所はundefined
で出力されています。青字はクライアントアプリに渡されるid_token
にデフォルトで含まれている項目です。ユーザー情報に関しては、id_token
にあって本オブジェクトにない項目は存在しません。クライアントアプリ側で知っておきたい青字以外の項目は、Rules
でid_token
に含めましょう。
context
各項目の説明やデータ型は、Context Object in Rulesを参照してください。伏せ字とundefined
ばかりで申し訳ありません。。
項目名 | 出力結果 |
context.tenant | {テナントのドメイン} |
context.clientID | Cfxxx |
context.clientName | SampleApp |
context.clientMetadata | {} |
context.connectionID | con_... |
context.connection | Username-Password-Authentication |
context.connectionStrategy | auth0 |
context.connectionOptions | {} |
context.connectionMetadata | {} |
context.samlConfiguration | {} |
context.protocol | oidc-basic-profile |
context.stats | {"loginsCount":17} |
context.sso | {} |
context.accessToken | {} |
context.idToken | {} |
context.original_protocol | undefined |
context.multifactor | undefined |
context.redirect | undefined |
context.sessionID | sp38... |
context.request | {"userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_1... |
context.primaryUser | undefined |
context.authentication | {"methods":[{"name":"pwd","timestamp":1566355207200}]} |
context.authorization | {"roles":[]} |
context.accessToken
とcontext.idToken
で、値は両方とも{}
です。これはRules
処理後に最終的にクライアントアプリに渡されるaccess_token
やid_token
の中身がセットされるためです。本項目は、id_token
にクレームを追加したり、access_token
にscopeを追加する用途として利用します。
context.stats
はsignup時にカウント1となります。
その他
auth0
Update Metadataには、以下のように説明されています。
auth0 Management API v2を呼び出すことができるauth0オブジェクト(node-auth0 SDKaのインスタンス)です。
出力したところ、以下ような値が確認できました。
{ accessToken: 'ey..', baseUrl: 'https://{テナントのドメイン}.auth0.com/api/v2', domain: '{テナントのドメイン}.auth0.com', users: { updateUserMetadata: [Function: updateUserMetadata], updateAppMetadata: [Function: updateAppMetadata] } }
node-auth0 SDKのインスタンスが初期化された状態で使えます。sdkのドキュメントは、こちらです。usersの中には、メソッドが2つあり、user_metadata
,app_metadata
の更新に利用可能です。詳細は、本項冒頭のリンクを参照してください。
accessTokenはJWT形式で、デコードすると以下の通りです。scopes
より、ManagementAPIでユーザー情報をread
,update
可能なトークンと分かります。この権限の範囲であれば、新たにMtoMでトークンを取得する必要はありません。「userオブジェクトにない項目の取得」「ユーザー情報を更新する必要がある」場合に、sdkのオブジェクト経由もしくは直接本トークンを利用しましょう。
自分が取得したい情報とそれに必要な権限の確認はManagementAPIを見る良いでしょう。
✻ Header { "typ": "JWT", "alg": "HS256" } ✻ Payload { "iat": 1566520463, "scopes": { "users": { "actions": [ "read", "update" ] } }, "jti": "xx...", "exp": 1566556463, "aud": "xx..." } ✻ Signature xxxxx...
configuration
Dashboard上で設定した資格情報やAPIキーなどの機密情報は、本オブジェクトを利用します。 詳細は、Store Configuration for Rulesを参照。
global
作成コストの高いオブジェクトを保存出来る機能です。詳細はCache Expensive Resources in Rulesで、MongoDB接続用のオブジェクトを維持する例が記載されています。 試してみたところ、1回目のログイン時にglobalに値を保存しておけば、2回目以降のログインでそのオブジェクトを利用できるということではなく、複数定義したRules間で値を共有出来るという理解です。
検証コードは下記の通りで、ルール1->ルール2の順番で実行しています。
ルール1
function (user, context, callback) { console.log(`login ${user.email}`); console.log(`foo is ${global.foo}`); if(global.foo){ console.log(`foo is ${global.foo}`); }else{ console.log("foo is not exists"); global.foo="bar"; console.log(`assignment ok ${global.foo}`); } callback(null, user, context); }
ルール2
function (user, context, callback) { console.log("===========2start=========="); console.log(`login ${user.email}`); console.log(`foo is ${global.foo}`); if(global.foo){ console.log(`foo is ${global.foo}`); }else{ console.log("foo is not exists"); global.foo="bar"; console.log(`assignment ok ${global.foo}`); } console.log("===========2fin========="); callback(null, user, context); }
出力結果
2:23:28 PM: new webtask request login [email protected] foo is undefined 2:23:28 PM: foo is not exists assignment ok bar ===========2start========== login [email protected] foo is bar <-- ルール1で格納した値が取得出来るている foo is bar ===========2fin========= 2:23:28 PM: finished webtask request
最後に
Rules
は認証周りの様々なシステム要件に対応可能です。同時にRules
の処理に無駄が多いと、ログイン処理時間が多くなり、ユーザーの負担となります。上記のようなオブジェクトを活用し、無駄な処理を書かないようにしましょう。