TIG DXユニット 1 アルバイトの小林です。
案件で認証プラットフォームであるAuth0を利用していますが、Auth0の機能の中でもRulesと呼ばれるユーザ認証時にJavaScriptの関数を走らせる事が出来る機能は非常に強力で様々なニーズに対応することが可能になります。
その中でJavaScriptの関数で書けるRulesに対して、ユニットテストを書く事が出来れば、Ruleの質も担保出来ます。
Auth0テナントへのRulesのexport、importにはauth0-deploy-cliを利用出来ますが、Ruleの記述方法が
function anyRule(user, context, callback) { |
の様な名前付き関数の形式なのでユニットテストの実現には工夫が必要となります。
今回はその工夫
の部分について書いていきたいと思います。
Auth0について
Auth0の概要についてはAuth0 導入編を、
Auth0 RulesについてはAuth0のRulesを使って認証認可を自在にカスタマイズするをそれぞれご参照ください。
前提
検証に利用したマシンのNode.jsのバージョンはv12.15.0
です。
検証に使うRule
以下の2つのRuleをテストしたいことにします。
- IDトークンのクレームに
{"https://example.com/color": "blue"}
を追加する、add-claims.js
function addClaim(user, context, callback) { |
http://example.com/some/api
にGETリクエストを送信して上手く行った場合はレスポンスをIDトークンのhttp://example.com/data
キーに入れる、request-example.js
- このRuleはリクエストのレスポンスが200以外の場合にログインエラーとする。
async function requestExample(user, context, callback) { |
コードについての補足
async function requestExample(user, context, callback) { |
Auth0 Rulesではトップレベルのasyncは許可
されています。
const axios = require('axios@0.19.2'); |
Auth0 Rulesではいくつかのライブラリが利用可能です。
利用可能なライブラリのリストはこちらに記載されています。
テスト環境
以下の様なNode.jsを用いたテスト環境を前提とします。
C:. |
それではこれらの前提を元に様々な手段について書いていきます。
記事中では引数のcallbackやスタブ化したrequireに適当なコードを書いていますが、実際にテストコードを書く際は引数のcallbackやスタブ化したaxiosをモック関数(jestでのjest.fn()
)にすることで動作の保証範囲をより広くすることが出来ます。
fsでスクリプトを文字列として取ってvmを利用して取り出す。
Auth0 docsに書かれている 2方法です。
add-claim.jsのRuleを呼び出すコードを書くとこのようになります。
const vm = require('vm'); |
テストしたいRuleのJavaScriptのソースコードを文字列として取得して、vm.runInThisContext
を使ってサンドボックス上にテスト対象のメソッドが返るコード
を実行します。先述の通りvm.runInThisContext
がテスト対象であるaddClaimメソッド
を返し、そのメソッドに(user,context,callback)を入れて呼び出しをしている形となります。
今回はコードを少しでも読みやすくするため、少し冗長な書き方をしていますが、コードが少し分かりにくいことを除けば比較的短いコードで記述することが可能になります。
requireモックについて
axiosモジュールを利用しているrequest-example.js
テストについて考えます。
HTTPリクエストが必要なユニットテストではクライアントライブラリをスタブにして様々なレスポンスが来た場合についてテストすることが品質向上に対して有効です。
そこで、require
モジュールをスタブにすることでaxios
では無く、独自のメソッドを利用出来る様にします。
嬉しいことに、vm
モジュールの仕様は外部のライブラリを利用するメソッドにおいても都合が良く、context
は同一であってもscopeは同一で無い
仕様があります。これにより、runInThisContext内ではrequireは未定義
になります。
requireに何か代入してaxiosモジュールのモック化を試みましょう。
・contextを新たに作成する。
runInThisContext
で目的のメソッドを取り出していましたが、require
が別の機能を果たす様なcontext
を別途作成して、それを利用します。
まとめると以下のコードで実現可能になります。
const vm = require('vm'); |
ここまでがAuth0 Docsに記載されているユニットテストの実現方法です。
rewire を利用する方法
他にはrewireを利用すると、vm
+fs
よりは裏ワザ感少な目で
テストコードを実行出来ます。
それでは早速add-claim.js
のメソッドを呼び出すコードを書いていきます。
const rewire = require('rewire'); |
何をやっているかは圧倒的に分かりやすいと思います。
続いてrequest-example.test.js
の呼び出しを進めていくのですが、rewire
はモック機能を持つので、
先程のvm
+fs
の組み合わせの時と同様にrequire
をモックすることで、axios
をモックします。
const rewire = require('rewire'); |
相変わらずrequire
のモックは泥臭いですが、ある程度は見やすくなったかと思います。
その他調査したもの
auth0-test-harness
auth0-rules-testharnessを用いてwebtask上でRuleを実行させることが出来てた様です。
webtaskとは
webtaskはAuth0 Rulesの実行環境としても使われており、webtaskの作成するサンドボックス上でRuleが実行されます。
私も早速試してみようと思ったのですが、publicなwebtaskのサービスが終了している様子 3 4のため、検証を断念したいと思います。
auth0-local-test-harness
auth0-rules-local-testharnessはauth0-rules-testharnessのwebtaskを使う所をlocalにした
物です。
コードをよく見ると、fs
+vm
でサンドボックス上で実行している物にauth0-authz-rules-apiが定義しているcontextを流し込んでいる様に見えます。
手元のマシンがNode.js v12.15で、Auth0 Rulesで使われるNode.jsのバージョンも執筆当時12.20.1 5 6ですが、手元でのnpm installが失敗するのと、npmパッケージが2年前から更新されていないことを考慮して断念します。
まとめ
今回は以下の4つの方法についてテスト方法の調査を行いました。
- Auth0 Docsに載っている方法
- rewireを利用した方法
- auth0-test-harnessを利用した方法
- auth0-local-test-harnessを利用
上2つが現実的な実装案になると考えていますが、
Auth0 Docsに載っている方法はfs
とvm
のシンプルな構成で利用可能な代わりに、コードが少し類雑、
rewireを利用した方法はシンプルに書けるが別途パッケージのインストールが必要と一長一短の様に見えます。
また、今回の調査においてはrequireのスタブ化が出来ても、ほぼ無理やり感は否めません。
1つのRuleが利用するモジュールが2つ以上の場合に置いて、与えられた引数から何を返すか場合分けで記述する必要があり、少し複雑です。
この辺りは今後の課題として、引き続き調査出来ればと思います。
Auth0の新機能 Actionsについて
執筆当時(2021/03/03)はBETA版機能ですが、認証認可を自由自在にカスタマイズする手段として、Rules,Hook に加えてActionsが存在します。
ActionsはRulesと同様ログイン時に何らかのスクリプトを走らせることが出来る機能です。
沢山の追加機能があるのですが、一部抜粋すると、
- バージョン管理の実装
- コードエディタの進化(コード補完、クイックヒント機能の搭載)
- 任意のnpmパッケージが利用可能
- スクリプトの記述方法がRulesと異なる
です。
詳しくはAuth0の公式ブログ: Introducing Auth0 Actions をご参照ください。
この記事で特筆すべき点はスクリプトの記述方法がRulesと異なる点です。
Actionは以下の形式で表記されています。
/** @type {PostLoginAction} */ |
Rulesとは異なり、Actionはmodule.exportsが記載されています。
つまり、rewireやfsを使わずともテスト対象のメソッドのインポートが出来ます。
インポート先でのrequireは、proxyquireなどを利用することでスタブ化が出来るため、これらを利用することでActionsの単体テストが実装可能になると考えられます。
まだBeta版であり、Auth0 Deploy CLIのSupported Featuresには記載されていませんが、Rulesよりも幅広い機能を持ち、改善されている点も多々あるため、今後はRulesの代わりにActionsの利用を視野に入れると良いと思います。
- 1.TIG: Technology Innovation Groupの略で、フューチャーの中でも特にIT技術に特化した部隊です。DXユニット: TIGの中にありデジタルトランスフォーメーションに関わる仕事を推進していくチームです。 ↩
- 2.Rules Testing Best Practices ↩
- 3.Future of rules without webtask.io - Auth0 Community ↩
- 4.Webtask ↩
- 5.Migrate to Node.js 12 ↩
- 6.Can I require? - Search which node modules you can use in webtask.ioに記載 ↩