developers

Auth0 RulesからAuth0 Actionsへの移行

Auth0 RulesからAuth0 Actionsへの移行を検討している方に向けたガイドラインをご案内します。

Auth0 RulesとHooksは2024年11月18日までに廃止されます。

変更内容の詳細については、RulesとHooksの提供終了についてをご覧ください。

Auth0 Actionsには、Rulesに比べて多くの利点があります。Rulesを使用してAuth0アイデンティティソリューションの動作をカスタマイズしている場合は、Actionsへの移行を検討する必要があります。この記事では移行の計画と実装方法について学習します。

Auth0 RulesとActionsの比較

Okta Customer Identity Cloud (Powered by Auth0)の強みの1つは、拡張性に焦点を当てていることです。例えば、ユーザーが標準動作をカスタマイズできる機能があげられます。Auth0 Rulesは、最初のツールとしてカスタムコードを実行し、認証機能を拡張できるようにしました。Rulesは、認証が成功した直後、トークンが発行される前に実行されるサーバーレスのコードブロックとして提供されています。

幅広い拡張性のニーズを満たすために、当社は常にツール群の改良に取り組んできました。最近では、カスタマイズに関して得た経験を統合し、ほとんどのコードベースのカスタマイズニーズに対応するAuth0 Actionsを開発者に提供しました。

Auth0 Actionsには、Rulesに比べて多くの利点があります。したがって、今後カスタマイズを始めようとしている場合には、Actionsが推奨ツールとなります。一方、Rulesを用いたカスタマイズを実装している場合は、Actionsへの移行準備を今から始めていただくことを強くお勧めします。Actionsに切り替えることで得られるいくつかの利点を見てみましょう。

  • Rulesは、処理の対象が認証フローのみです。Auth0 Actionsを使用すると、認証、事前および事後のユーザー登録、パスワード変更、クライアント資格情報の交換、SMS送信などの 複数のフロー を処理できます。
  • Rulesには統合バージョン管理システムがありません。Actionsではバージョン管理機能を提供しています。このため、本番環境のコードに影響を与えることなく コードの変更や複数バージョン作成、以前のバージョンへのロールバックを行えます。
  • Rulesとは異なり、Actionsエディターはコードヒント、すべてのnpmパッケージ、および分離されたシークレット情報の管理をサポートします。このため、開発者エクスペリエンスが大幅に向上 します。

Auth0 Actionsの簡単な紹介については、このブログ投稿をご覧ください。

Auth0 Actionsは改善されたカスタマイズ環境をもたらしますが、RulesとHooksは提供終了日まで引き続き利用可能であり、Actionsと共存できます。RulesとHooksはActionsの前に実行されることに注意してください。

Actionsを使用してRulesを置き換える際にいくつかの制限事項に注意してください。

  • Rulesでは、
    user
    オブジェクトと
    context
    オブジェクトにプロパティを追加し、パイプライン内の他のRulesとデータを共有できます。これらのプロパティは、後続のRulesでアクセスできます。Actionsでは同様の仕組みを使うことはできませんが、ユーザーメタデータにアクセスし、後続のActionsとデータを共有できます。
  • RulesはSAMLアサーションまたは属性を変更できますが、Actionでは同じ機能は提供されていません。
  • RulesはAuth0 Management API への制限付きアクセスを有しており、また、
    global
    および
    auth0
    オブジェクト
    にアクセスできます。Actionsは同様の権限やアクセスを持ちませんが、以下で説明する代替アプローチがあります。

より詳しい詳細については現在の実装の制限事項の完全なリストおよびAuth0 Rules to Actions移行ガイドをご覧ください。代替方法の無いほとんどの機能差異についてはRules終了までにActionsで行えるようにする予定です。今後のアップデートをお待ちいただくか、営業・サポートにお問い合わせください。

これらの注意点を念頭に置いて、RulesをAuth0 Actionsに変換する方法を見てみましょう。

ノーコードソリューション

RulesからActionsに移行する場合、Auth0マーケットプレイスで提供されているノーコードインテグレーション(Actionsを用いたインテグレーションを含む)も検討できます。弊社パートナーが構築したインテグレーションはユーザーの同意管理、プログレッシブプロファイリング、ユーザー検証などの一般的なIDユースケースを解決します。これらのインテグレーションを利用することで、カスタムコードを利用した場合に比べて開発コストとメンテナンスコストを削減しつつ、迅速な市場投入を可能にします。

コード変換ガイドライン

Auth0 Rulesを使用すると、認証フローをカスタマイズできます。そのため、RulesはログインフローにおけるPost Login Triggerを処理するAuth0 Actionsに変換できます。

この記事では、Actionsの作成またはRulesの移行を段階的に説明していません。Rulesの作成方法とActionsの作成方法について基本を理解していることを前提としています。 そうでない場合は、Rulesの作成およびActionsの作成に関するAuth0ドキュメントを参照してください。 どちらの場合も、Auth0アカウントが必要です。アカウントはこちらから無料で登録できます。

Post LoginトリガーのActionsエディターは次のようになります。

Auth0 Actions エディター

Rules移行の基本的な考え方として、Rulesを実装する関数の本体を次の関数の本体に移動します。

exports.onExecutePostLogin = async (event, api) => {
};

この場合、コードの単なるコピー&ペーストでは正しく動作しません。いくつかの変更を適用する必要があります。そのため、ここからは既存のRulesコードに対して実施する特定の変更について取り上げます。

これまで認証フローは複数のRulesを使って処理できました。Actionsについても複数のActionを利用できます。そのため既存のRulesを順序通りActionsにマッピングし同じ機能を維持できます。

パラメータのマッピング

Auth0 Rulesは、

user
context
callback
の3パラメータを持つJavaScript関数でした。Actionsを実装する
onExecutePostLogin()
関数では、
event
api
という2つのパラメータを持ちます。名前が異なるという点以外に、これらのパラメーターをどのようにマッピングすれば良いでしょうか。また、ActionsはどのようにしてRulesと同じデータをパラメータから取得できるのでしょうか。

Actionsではその設計において、実行環境から関数に渡されるデータが統合されています。原則として、Rulesの

user
オブジェクトと
context
オブジェクトのすべての読み取り専用プロパティは、Actionsの
event
オブジェクトに移動されています。ログインの失敗やユーザーメタデータの更新など、実行環境とのインタラクションは
api
オブジェクトのメソッドを通じて実装されています。

event
オブジェクトおよび
api
オブジェクトの詳細についてはドキュメントを参照してください。

ユーザーデータへのアクセス

Rulesでは、

user
オブジェクトパラメータにアクセスし現在のユーザーに関するデータを取得します。Actionsでは、
event.user
オブジェクトにアクセスする必要があります。ここでは、外部IDプロバイダーまたはカスタム データベース スクリプトによって追加された属性を除き、
user
オブジェクトと同じデータを見つけることができます。

例として、次のRulesコードがあるとします。

function myRule(user, contect, callback) {
    const userEmail = user.email;
    const userId = user.user_id;

    // ... 追加コード
}

この実装は次のようなActionsコードに変換できます。

exports.onExecutePostLogin = async (event, api) => {
    const userEmail = event.user.email;
    const userId = event.user.user_id;

    // ... 追加コード
};

user.app_metadata
オブジェクトはRulesでは定義されていない可能性がありますが、対応する
event.user.app_metadata
オブジェクトは常にActionsで定義されることに注意してください。

コンテキストデータへのアクセス

Rulesでは、

context
オブジェクトに現在のログインセッションに関するデータが保存されます。Actionsでは、同じデータを
event
オブジェクトで取得できます。

次のRules関数を考えてみましょう。

function myRule(user, context, callback) {
    const clientId = context.clientID;
    const clientMetadata = context.clientMetadata || {};

    const connectionId = context.connectionID;
    const connectionMetadata = context.connectionMetadata || {};

    const protocol = context.protocol;

    const tenant = context.tenant;

    // ... 追加コード
}

Actionでは以下のようになります。

exports.onExecutePostLogin = async (event, api) => {
    const clientId = event.client.client_id;
    const clientMetadata = event.client.metadata;
    
    const connectionId = event.connection.id;
    const connectionMetadata = event.connection.metadata;
    
    const protocol = event.transaction.protocol;
    
    const tenant = event.tenant.id;

    // ... 追加コード
};

ご覧のとおり、

context
オブジェクトの一部のプロパティは、
event
オブジェクトにアタッチされたいくつかのオブジェクトのプロパティになっています。たとえば、
client
オブジェクト、
connection
オブジェクトなどがあります。ログインセッションデータは、より明確に整理されるようリファクタリングされています。

一部のプロパティ名が異なることに注意してください。たとえば、

clientID
client_id
に、
connectionMetadata
connection.metadata
と変更されています。

必要に応じて、Rulesの

context
オブジェクトActionsの
event
オブジェクト
を参照して、それぞれの違いについて詳細を確認してください。

依存関係の処理

Auth0 Rulesではさまざまな機能を追加するために依存関係を含めることができます。数千のnpmパッケージを利用できますが、npm レジストリで利用可能なすべてのパッケージを利用できるわけではありませんでした。Rulesで利用できないパッケージ(またはnpmパッケージの特定のバージョンのみ)が必要な場合は、その旨をAuth0にリクエストする必要がありました。

Actionsでは、npmレジストリ内の利用可能な100万以上にもおよぶすべてのパッケージを依存関係として追加できます。

また、Rulesとは異なり、Actionsではコード内でパッケージのバージョンを指定する必要はありません。たとえば、Rulesでは次のように依存関係を宣言する必要がありました。

function myRule(user, contect, callback) {
    const axios = require("axios@0.22.0");

    // ... 追加コード
}

Actionsでは、エディターのモジュールマネージャーでパッケージのバージョンを指定します。そのため、コードは次のようになります。

const axios = require("axios");

exports.onExecutePostLogin = async (event, api) => {
    
    // ... 追加コード
};

ここでは依存関係を関数の本体の外側でNode.jsのスタイルでインポートしていることに注意してください。

Rulesコールバックの変換

Rulesでは、

callback
パラメータを使用して処理の結果を返します。たとえば、次のRulesを実行することになるAuth0認証プロセスへ制御を渡す場合は、現在の
user
オブジェクトと
context
オブジェクトを渡して
callback()
関数を呼び出す必要があります。現在のログインプロセスを停止したい場合は、エラーインスタンスを指定して
callback()
関数を呼び出す必要があります。

Actionsを使用するとすべてがより簡単になります。制御を呼び出し元に渡す場合、

return
を使用します。ログインプロセスを停止するには、
api.access.deny()
メソッドを呼び出します。

次のRules関数を考えてみましょう。

function myRule(user, contect, callback) {
    const userAppMetadata = user.app_metadata || {};

    if (userAppMetadata.condition === "success") {
        // このRulesは成功しました。次のRulesに進みます。
        return callback(null, user, context);
    }

    if (userAppMetadata.condition === "failure") {
        // このRulesは失敗したため、エラー応答でログインを停止します。
        return callback(new Error("失敗メッセージ"));
    }

// ... 追加コード
}

このコードは、

app_metadata
オブジェクトの
condition
プロパティの値に基づいて成功または失敗を判断します。Actionsでは次のように変換できます。

exports.onExecutePostLogin = async (event, api) => {
    if (event.user.app_metadata.condition === "success") {
        // このActionsは成功しました。次のActionsに進みます。
        return;
    }

    if (event.user.app_metadata.condition === "failure") {
        // このActionsは失敗したため、エラー応答でログインを停止します。
        return api.access.deny("失敗メッセージ");
    }

    // ... 追加コード
};

Rules内の

callback()
インスタンスについて、失敗した場合は
api.access.deny()
への呼び出しに、成功した場合は
return
に置き換える必要があります。また、コードが関数本体の最後まで実行される場合は、
return
ステートメントを省略できます。

変換例

これまでのRulesのコードをActionsに変換するための一般的なガイドラインをもとにいくつかの例を見てみましょう。

カスタムクレームをトークンに追加する

Rulesの最も一般的な使用例の1つは、IDトークンまたはアクセストークンのいずれかのトークンにカスタムクレームを追加する機能です。たとえば、次のコードスニペットは、IDトークンに2つのカスタムクレームを追加するRulesを示しています。

function myRule(user, context, callback) {
    const namespace = 'https://myapp.example.com/';

    context.idToken[namespace + 'favorite_color'] = user.user_metadata.favorite_color;
    context.idToken[namespace + 'preferred_contact'] = user.user_metadata.preferred_contact;
    callback(null, user, context);
}

このRulesは、次のようにActionsに変換できます。

exports.onExecutePostLogin = async (event, api) => {
    const namespace = 'https://myapp.example.com/';

    api.idToken.setCustomClaim(
        `${namespace}/favorite_color`,
        event.user.user_metadata.favorite_color
    );

    api.idToken.setCustomClaim(
        `${namespace}/preferred_contact`、
        event.user.user_metadata.preferred_contact
    );
};

Rulesでは値が

context.idToken
オブジェクトに直接割り当てられますが、Actionsでは
api.idToken.setCustomClaim()
メソッドを使用する必要があります。さらに
callback()
呼び出しがActions内では消えていることにも注目してください。

メタデータの更新

Rulesを使用して、ユーザーとアプリケーションのメタデータを管理できます。次の例は、ユーザーのメタデータにフォントサイズ設定を保存する方法を示しています。

function myRule(user, context, callback){
    user.user_metadata = user.user_metadata || {};
    user.user_metadata.preferences = user.user_metadata.preferences || {};
    user.user_metadata.preferences.fontSize = 12;

    // user_metadataの更新を永続化します
    auth0.users.updateUserMetadata(user.user_id, user.user_metadata)
        .then(function(){
        callback(null, user, context);
        })
        .catch(function(err){
        callback(err);
        });
}

対応するActionsのコードは次のようになります。

exports.onExecutePostLogin = async (event, api) => {
    const userPreferences = event.user.user_metadata.preferences || {};

    userPreferences.fontSize = 12;

    // user_metadata の更新を永続化します
    api.user.setUserMetadata("preferences", userPreferences);
};

RulesのコードはPromiseを返す

auth0.users.updateUserMetadata()
メソッドを使用しますが、Actionsのコードは
api.user.setUserMetadata()
メソッドを使用してメタデータ値を割り当てます。ActionsのコードがRulesのコードと比べて単純化されていることに注目してください。

Rulesでは

auth0.users.updateUserMetadata()
を呼び出すたびに、Auth0 Management APIへのHTTPリクエストが作成されます。一方、1つ以上のActionsで発生する
api.user.setUserMetadata()
のすべての呼び出しは、Actionsの実行の最後に単一のAPI呼び出しになります。これは、レート制限エラーが発生するリスクの軽減に役立ちます。

ユーザーのリダイレクト

ログインプロセスを完了し、認証を再開するためにユーザーを別のページにリダイレクトする必要がある場合、Rulesでは次のように実装します。

function myRule(user, context, callback) {
    if (context.protocol === "redirect-callback") {
        // ユーザーは /continue エンドポイントにリダイレクトされました
        user.app_metadata.wasRedirected = true;
        return callback(null, user, context);
    } else if (
        context.protocol === "oauth2-password" ||
        context.protocol === "oauth2-refresh-token" ||
        context.protocol === "oauth2-resource-owner"
    ) {
        // ユーザーはリダイレクトできません
        return callback(null, user, context);
    }

    // ユーザーは直接ログインしています
    if (!user.app_metadata.wasRedirected) {
        context.redirect = {
            URL: "https://example.com",
        };
        callback(null, user, context);
    }
}

このRulesや、他の以前のRulesは、リダイレクト前と応答時に2回実行されます。通常はリダイレクトと応答を処理するロジックを同じRulesに入れる必要があります。また、現在のプロトコルがユーザーリダイレクトに適しているかどうかを手動で確認する必要があります。

Actionsに変換すると、次のようになります。

exports.onExecutePostLogin = async (event, api) => {
    if (!event.user.app_metadata.wasRedirected && api.redirect.canRedirect()) {
    api.redirect.sendUserTo("https://example.com");
    }
};

exports.onContinuePostLogin = async (event, api) => {
    api.user.setAppMetadata("wasRedirected", true);
};

リダイレクト(

onExecutePostLogin()
)と応答(
onContinuePostLogin()
)を処理するそれぞれの関数があります。また、現在のプロトコルがユーザーリダイレクトに適しているかどうかを確認する負担は、
api.redirect.canRedirect()
メソッドが担います。さらに、Actionsは
context.redirect
オブジェクトの代わりに
api.redirect.sendUserTo()
メソッドを使用してユーザーをリダイレクトすることに注意してください。

Actionsを使用したユーザーリダイレクトの詳細については、ドキュメントを確認してください。

移行戦略を計画する

ここまでの内容でRulesとActionsで使用されるコードの主な違いがより明確になりました。ただし、RulesをActionsに変換する方法を知ることだけが注意すべきことではありません。実稼働環境での問題を回避するために、コードをRulesからActionsに移行する戦略を計画する必要があります。

ぜひ、RulesからActionsへの移行戦略を定義するためのガイドラインに関するドキュメントを確認してください。

まとめ

この記事では、Auth0 RulesからActionsへの移行プロセスを設定するための基礎を説明しました。また、移行する主な理由と、RulesのコーディングとActionsのコーディングの基本的な違いについて説明しました。

いくつかの変換例では、Actionsがどのようにコードを簡素化するかを示しました。その他の変換例と追加情報は、Auth0 Rules to Actions移行ガイドをご確認ください。

このブログはこちらの英語ブログからの翻訳、池原 大然によるレビューです。さらに2023年7月現在の状況から内容について変更・追記しています。