Auth0のRulesをActionsに移行してみる #Auth0JP #Auth0アドカレ

[Auth0 Advent Calendar 2020](https://qiita.com/advent-calendar/2020/auth0) 1日目の記事です
2020.12.01

現在ベータ版としてActionsの機能が公開されています。

将来、RulesからActionsへの移行を検討しないといけない時期が来るかもしれないので、この段階で試してみようと思います。

シナリオ

簡易なアプリケーションで、メールアドレスドメインのホワイトリストでログインできるアドレスを制限し、多要素認証を行うログインフローカスタマイズをRulesで行っているとします。

Ruleスクリプト

メールアドレスドメインのホワイトリスト

classmethod.jpじゃないとログインできないようにする。

function emailDomainWhitelist(user, context, callback) {
  if(context.clientID !== '<アプリケーションのクライアントID>'){
    return callback(null, user, context);
  }

  // Access should only be granted to verified users.
  if (!user.email || !user.email_verified) {
    return callback(new UnauthorizedError('Access denied.'));
  }

  const whitelist = ['classmethod.jp']; //authorized domains
  const userHasAccess = whitelist.some(
      function (domain) {
        const emailSplit = user.email.split('@');
        return emailSplit[emailSplit.length - 1].toLowerCase() === domain;
      });

  if (!userHasAccess) {
    return callback(new UnauthorizedError('Access denied.'));
  }

  return callback(null, user, context);
}

多要素認証

プッシュ、SMS、音声、OTPまたはEメールでの多要素認証を有効にする

function multifactorAuthentication(user, context, callback) {
  if(context.clientID !== '<アプリケーションのクライアントID>'){
    return callback(null, user, context);
  }
    
  context.multifactor = {
      provider: 'any',
      // optional, defaults to true. Set to false to force authentication every time.
      // See https://auth0.com/docs/multifactor-authentication/custom#change-the-frequency-of-authentication-requests for details
      allowRememberBrowser: false
  };

  callback(null, user, context);
}

現状のフローを確認

どのMFAを使うのかという設定は以下の画像のようにしています。

ホワイトリスト以外のドメインでログイン

gmailのアドレスでログインすると、

このようにエラー内容がURLのパラメータについてコールバック先に返却されています。ログインも完了しません。

多要素認証

メールアドレスのドメイン確認を突破した後、以下のように多要素認証を行う画面に遷移します

Actionsに置き換える

Rulesでの動作は正常に終えたので、このフローを実際にActionsに移行してみます。 使用するフローはLoginです。

Actionを作っていきましょう

メールアドレスドメインのホワイトリスト

Actionを作成すると、以下のコードが最初から書かれているので、これをカスタマイズしていきます。

/** @type {PostLoginAction} */
module.exports = async (event, context) => {
  return {};
};

eventactions-event-objectに含まれるプロパティを使用でき、

contextactions-context-objectに含まれるプロパティを使用できます。

Ruleと同じようなやり方でAction化してみました。

/** @type {PostLoginAction} */
module.exports = async (event, context) => {
  if(event.client.id !== '<アプリケーションのクライアントID>'){ //クライアントIDはSecretsに入れてもいいかな
    return {};
  }

  // Access should only be granted to verified users.
  if (!event.user.email || !event.user.emailVerified) {
    throw new Error('Logins are not allowed.');
  }

  const whitelist = ['classmethod.jp']; //authorized domainsもSecretsに入れてもいいかな
  const userHasAccess = whitelist.some(
      function (domain) {
        const emailSplit = event.user.email.split('@');
        return emailSplit[emailSplit.length - 1].toLowerCase() === domain;
      });

  if (!userHasAccess) {
    throw new Error('Logins are not allowed.');
  }
  
  return {};
};

ログインユーザーの情報は、eventオブジェクトから取得しています。 また、Ruleではcallback関数を使ってエラーを返していましたが、Actionsではthrowでerrorを返すようです。

Actionをデプロイした後、以下のようにLoginフローに組み込みます。

多要素認証

多要素認証も同様にRuleと同じようなやり方でAction化をしてみました。

/** @type {PostLoginAction} */
module.exports = async (event, context) => {
  if(event.client.id !== '<アプリケーションのクライアントID>'){ //クライアントIDはSecretsに入れてもいいかな
    return {};
  }

  return {
    command: {
      type: "multifactor",
      provider: "any"
    }
  };
};

同様にフローに組み込みます(EmailDomainWhitlistの下)

Actionフローの確認

ActionsはRulesの後に動くので、Rulesのスクリプトが起動しないようにdisableにしてから行います。

Rulesと同じような手順でログインしてみると、同じようにエラーになります(ホワイトリスト以外のドメインでログイン)。

Auth0のログにもエラーが出力されていることがわかります。

多要素認証も、Rulesと同様にメールアドレスのドメイン確認を突破したらMFAの画面に遷移します。

まとめ

Rulesでカスタマイズしていた処理をActionsに書き換えてみました。 コールバック方法やイベントオブジェクトの取得方法は異なりますが、同じnodejsでかけるのでそれほど苦労しないで移行は可能だと思います。

※ まだベータ版なので、本番環境ではなく開発環境やテスト環境で試しましょう