Auth0のRulesで使えるオブジェクトを出力してみた!

2019.08.26

前提

今回は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 xxxxxxxx@gmail.com
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 xxxxxxxx@gmail.com
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にあって本オブジェクトにない項目は存在しません。クライアントアプリ側で知っておきたい青字以外の項目は、Rulesid_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.accessTokencontext.idTokenで、値は両方とも{}です。これはRules処理後に最終的にクライアントアプリに渡されるaccess_tokenid_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 xxxxxx@xxxxxx.jp
foo is undefined
2:23:28 PM: foo is not exists
assignment ok bar
===========2start==========
login xxxxxx@xxxxxx.jp
foo is bar <-- ルール1で格納した値が取得出来るている
foo is bar
===========2fin=========
2:23:28 PM:
 finished webtask request

最後に

Rulesは認証周りの様々なシステム要件に対応可能です。同時にRulesの処理に無駄が多いと、ログイン処理時間が多くなり、ユーザーの負担となります。上記のようなオブジェクトを活用し、無駄な処理を書かないようにしましょう。