RulesからActionsに移行する

既存のルールをアクションに変換する場合は、新しいアクションをログインフローのログイン後(post-login)トリガーに関連付ける必要があります。以下の手順に従い、アクションを元のルールと同じ順序にしておくと、機能は同じになります。

移行を計画する

Post-Loginアクションは既存のルールの後に実行されるため、Dashboardでルールを一度に1つずつ変換することも、 Management APIを使用して一度にすべてを変換することもできます。

コードを変換してから、アクションをアクティブにし、ルールを非アクティブにする必要があります。アクションの有効化とルールの無効化は、連続してすばやく実行できますが、順序によっては、両方が実行されない、またはどちらも実行されない時間が短期間発生する場合があります。

このため、パイプラインを段階的に移行することをお勧めします。ルールコードの一部をアクションコードに変換し、ステージング環境でテストしてから、一度に1つずつライブに移行します。アクティブなルールはデプロイされたアクションの前に実行されるため、ルールパイプラインの最後から開始して逆方向に作業すると、アクションで他のロジックを構築およびテストするときに、一部のロジックをルールに保持したままにすることができます。

移行を計画するときのヒント

  • アクションとルールを1対1にして、機能のオン・オフやテストをひとまとめに実行できるようにします。

  • ユーザーメタデータにフラグを使って、コストがかかる操作や1回限りの操作が重複しないようにします。

  • Rulesのパイプラインの最後から始めて、逆方向に作業します。アクティブなRulesはAアクションのデプロイ前に実行されるため、構築の際にはRules内にいくつかのロジックを維持させて、他のロジックをActionsでテストすることができます。

  • 変更は、影響やトラフィックが最も少ない時間帯や時期に行います。

  • 移行期間が無効なログインや保護の欠落に繋がるようであれば、一時的にログインページをカスタマイズして、ログインを停止することを検討します。

  • 移行のスクリプトを作成し、一括または繰り返してテストと実装が行えるように、Auth0 Deploy CLIの使用を考慮します。

制限事項を理解する

アクションはルールでできることの大部分を処理できますが、移行を開始する前にいくつかの制限事項に注意する必要があります(注:フックとアクションはどちらも移行中に実行できます)。

  • ルールは、後続のルールでアクセスできるユーザーオブジェクトとコンテキストオブジェクトにプロパティを追加できます。個々のアクションが別のアクションとデータを直接共有することはできません。

  • Actionsは、アクセストークンやAPI応答などのデータを複数の実行にわたって保持することはできません。

  • アクションには、ルールの場合のように、Management APIのアクセストークングローバルauth0オブジェクトへのアクセスは提供されません。Management API呼び出しを引き続き行う方法については、「コードの変換」セクションを参照してください。

すべての制限事項については、「Actionsの制限事項」を参照してください。

コードを変換する

ルールをアクションに変換するには、ルール固有のコードをアクションコードに置き換える必要があります。このセクションでは、機能するルールを同等のアクションに変換するためのタスクについて説明します。

コードを変換するときのヒント

  • 一般的に、Actionsのevent オブジェクトについて、Rulesのusercontextオブジェクトの読み取り専用プロパティを確認します。apiオブジェクトの機能について、Actionsがシステムに及ぼす副次的な影響(ログインの失敗、ユーザーメタデータの更新など)を確認します。

  • コードを書くときは、Auth0 DashboardのActions Code Editorを使います。エラーがハイライトされたり、オートコンプリート機能によって提案が自動入力されたりするので、便利です。

  • 稼働させる前に、新しいActionsのテストステージング環境やテスト環境で念入りに行います。

ルールコードを新しいアクションにコピーする

  1. 運用テナントにログインし、変換元となるルールのコードをコピーします。

  2. 非運用テナントに切り替えて、[Auth0 Dashboard] > [Actions(アクション)] > [Library(ライブラリ)]に移動します。

  3. [Build Custom(カスタムの構築)]を選択した後、

    • 変換するルールの名前と一致するアクションの名前を入力します。

    • トリガーを見つけて、ログイン/ログイン後を選択します。

    • [Runtime(ランタイム)]で、[Node 16]を選択します。

    • [Create(作成)]を選択します。

  4. アクションコードエディターのコードブロックで、エクスポートされたonExecutePostLogin関数の下に変換するルールコードを貼り付けます。

  5. コードを関数に移動するときに、この記事で後述している変更を加えます。

関数宣言を変更する

ルールは、ユーザーコンテキスト、およびコールバックパラメータを持つ単純な宣言済み関数を使用しますが、アクションは特定の名前にエクスポートされた関数を使用します。以下の変更を加えます。現在のところ、表示されるエラーは無視してください。

変更前

async function myRulesFunction(user, context, callback) {
    // ... additional code
}

Was this helpful?

/

変換後

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

Was this helpful?

/

ユーザーデータへのアクセス方法を変更する

ルールでは、ログインするユーザーに関するデータはuserオブジェクトに保存されます。アクションでは、このデータはeventオブジェクトuserプロパティにあります。既存のプロパティの大部分は、この新しい場所でアクセスできます。

変更前

function myRulesFunction(user, context, callback) {
	const userEmail = user.email;
	const userId = user.user_id;

	// This property could be undefined in Rules.
	const userAppMetadata = user.app_metadata || {};

	// ... additional code
}

Was this helpful?

/

変換後

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

	// This property will never be undefined in Actions.
	const userAppMetadata = event.user.app_metadata;

	// ... additional code
};

Was this helpful?

/

コンテキストデータへのアクセス方法を変更する

ルールでは、現在のログインセッションに関するデータはcontextオブジェクトに保存されます。アクションの場合、このデータは再形成され、eventオブジェクトに移動されました。プロパティの多くはそのまま移行されましたが、わかりやすくするために一部が結合されました。

変更前

function myRulesFunction(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;

	// ... additional code
}

Was this helpful?

/

変換後

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;

	// ... additional code
};

Was this helpful?

/

依存関係を変換する

ルールには、requireステートメントにバージョン番号を含めることを要求する方法で依存関係が含まれます。アクションでは、より標準的なCommonJS構文が使用され、コードエディターの外部でバージョンを示す必要があります。

ルールでは、特定のパッケージの特定のバージョンのみが許可され、新しいパッケージとバージョンを追加するにはAuth0への要求が必要です。アクションでは、npmレジストリで利用可能な任意のパッケージを要求できます。

  1. ルールコード内でrequireステートメントを検索します。

  2. バージョン番号を削除しますが、これらの番号はメモしておいてください。

  3. 依存関係がコアNodeJSモジュールでない場合は、「初めてアクションを作成する」の「依存関係を追加する」セクションの手順に従って依存関係を追加してください。依存関係がコアNodeJSモジュールである場合は、追加する必要はありません。

  4. 見つかったrequireステートメントをfunction宣言の外に移動します:

変更前

function myRulesFunction(user, context, callback) {
	const dependency = require("dependency@1.2.3");

	// ... additional code
}

Was this helpful?

/

変換後

const dependency = require("dependency"); // v1.2.3
exports.onExecutePostLogin = async (event, api) => {
	// ... additional code
};

Was this helpful?

/

コールバックを変換する

ルールの処理が完了したら、callback()関数を呼び出して、ログインに失敗した場合はエラーを渡す必要があります。逆に、アクションは成功時に返したり、ログインに失敗した場合はメッセージとともにapiメソッドを呼び出したりできます。ルール内のcallback()のすべてのインスタンスは削除するか、失敗した場合はapi.access.deny()に置き換える必要があります。ルールとアクションの両方で、特定の条件で処理を停止する必要がある場合は、returnステートメントを使用します。

変更前

function myRulesFunction(user, context, callback) {
	const userAppMetadata = user.app_metadata || {};
	if (userAppMetadata.condition === "success") {
		// This Rule succeeded, proceed with next Rule.
		return callback(null, user, context);
	}

	if (userAppMetadata.condition === "failure") {
		// This Rule failed, stop the login with an error response.
		return callback(new Error("Failure message"));
	}

	// ... additional code
}

Was this helpful?

/

変換後

exports.onExecutePostLogin = async (event, api) => {
	if (event.user.app_metadata.condition === "success") {
		// This Action succeeded, proceed with next Action.
		return;
	}

	if (event.user.app_metadata.condition === "failure") {
		// This Action failed, stop the login with an error response.
		return api.access.deny("Failure message");
	}

	// ... additional code
};

Was this helpful?

/

シークレットの処理を変更する

ルールでは、構成値をグローバルに設定します。これは、すべてのルールがすべてのシークレット値にアクセスできることを意味します(詳細については、「ルールの構成を保管する」を参照してください)。アクションでは、個々のアクションごとに構成値を設定します。アクションのコンテキスト外からアクションのシークレット値にアクセスすることはできません。

シークレットをルールからアクションに変換するには:

  1. 操作中の特定のアクションに必要な値を保存します。

  2. アクション内からアクセスする必要がある値ごとにシークレットを追加します。方法については、「初めてアクションを作成する」の「シークレットを追加する」セクションをお読みください。

  3. コードを変換します。

変更前

function myRulesFunction (user, context, callback) {
  const { CLIENT_ID, CLIENT_SECRET } = configuration;

  // ... additional code
}

Was this helpful?

/

変換後

exports.onExecutePostLogin = async (event, api) => {
  const { CLIENT_ID, CLIENT_SECRET } = event.secrets;

  // ... additional code
}

Was this helpful?

/

ルールの場合と同様に、Auth0は保管時のすべてのシークレット値を暗号化します。

カスタムクレームをトークンに変換する

ルールとアクションでは共に、カスタムクレームをIDトークンおよびアクセストークンに追加できます。ルールでは、これはcontextオブジェクトのプロパティですが、アクションではapiオブジェクトのメソッドが使用されます。

変更前

function myRulesFunction(user, context, callback) {
	const userAppMetadata = user.app_metadata || {};
	const namespace = "https://namespace/";

	context.idToken[`${namespace}/emp_id`] = userAppMetadata.emp_id;
	context.accessToken[`${namespace}/emp_id`] = userAppMetadata.emp_id;

	// ... additional code
}

Was this helpful?

/

変換後

exports.onExecutePostLogin = async (event, api) => {
	const namespace = "https://namespace/";

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

	api.accessToken.setCustomClaim(
		`${namespace}/emp_id`, 
		event.user.app_metadata.emp_id
	);

	// ... additional code
};

Was this helpful?

/

多要素トリガーを変換する

ルールでは、contextオブジェクトmultifactorプロパティを変更することで、多要素認証をトリガーできます。アクションでは、これはapiオブジェクトのメソッドを使用して実行されます。

変更前

function myRulesFunction(user, context, callback) {
	if (user.app_metadata.needs_mfa === true) {
		context.multifactor = { 
			provider: "any", 
			allowRememberBrowser: false,
		};
	}

	// ... additional code
}

Was this helpful?

/

変換後

exports.onExecutePostLogin = async (event, api) => {
	if (event.user.app_metadata.needs_mfa === true) {
		api.multifactor.enable("any", { allowRememberBrowser: false });
	}

	// ... additional code
};

Was this helpful?

/

ユーザーメタデータの更新を変換する

ルール内のuser_metadataプロパティとapp_metadataプロパティを更新するには、Management APIの呼び出しが必要であり、レート制限エラーが発生する可能性があります。ただし、アクションは複数のユーザーメタデータの変更を示す方法を提供しますが、Management APIを呼び出すのは1回だけです。

変更前

function myRulesFunction(user, context, callback) {
	user.app_metadata = user.app_metadata || {}; 
	user.app_metadata.roles = user.app_metadata.roles || [];
	user.app_metadata.roles.push("administrator"); 

	auth0.users
		.updateAppMetadata(user.user_id, user.app_metadata) 
		.then(() => callback(null, user, context))
		.catch((err) => callback(err));

	// ... additional code
}

Was this helpful?

/

後続のルールでユーザーメタデータを更新する必要がある場合は、Management APIを個別に呼び出す必要があるため、レート制限に達する可能性が高くなります。

変換後

exports.onExecutePostLogin = async (event, api) => {
	const userRolesUpdated = event.user.app_metadata.roles || [];
	userRolesUpdated.push("administrator"); 

	// Note the two different methods here. 
	api.user.setAppMetadata("roles", userRolesUpdated);
	api.user.setUserMetadata("hasRoles", true);

	// ... additional code
};

Was this helpful?

/

後続のアクションでユーザーメタデータを更新する必要がある場合は、api.user.setUserMetadataまたはapi.user.setAppMetadataを呼び出す必要があります。アクションでは、1つまたは複数のアクションにわたってこれらの関数を複数回呼び出すと、フローが完了したときに1つのManagement API呼び出しが行われます。

他のManagement API呼び出しを変換する

一般に、ルールやアクションなどの高トラフィックのクリティカルパスからManagement APIを呼び出すことはお勧めしません。拡張ポイントからの呼び出しを含め、すべてのAuth0 APIへの要求にはレート制限があり、すべてのログインに対してAPIを呼び出すと、トラフィックが多いときにログインが失敗する可能性があります。

ただし、呼び出しが必要で、それらがレート制限を回避するように構成されている場合は、アクション内からManagement APIを呼び出すことができます。前述の「制限事項を理解する」セクションで説明しているように、アクションにはManagement APIのアクセストークンが提供されないため、アクションを有効にする前にアクセストークンを取得する必要があります。

  1. M2Mアプリケーションを登録し、Management APIに対して認可します

  2. アクションにクライアントIDクライアントシークレットを保存します。

  3. Management APIのアクセストークンを取得します

  4. Management APIを呼び出します。

変更前

function myRulesFunction(user, context, callback) {
	const ManagementClient = require("auth0@2.9.1").ManagementClient; 
	const managementClientInstance = new ManagementClient({
		// These come from built-in Rules globals
		token: auth0.accessToken, 
		domain: auth0.domain,
	}); 

	managementClientInstance.users.assignRoles(
		{ id: user.user_id }, 
		{ roles: ["ROLE_ID_TO_ADD"] }, 
		(error, user) => {
			if (error) {
				return callback(error);
			}

			// ... additional code
		}
	);
}

Was this helpful?

/

変換後

const auth0Sdk = require("auth0");
exports.onExecutePostLogin = async (event, api) => {
	const ManagementClient = auth0Sdk.ManagementClient;

	// This will make an Authentication API call
	const managementClientInstance = new ManagementClient({
		// These come from a machine-to-machine application
		domain: event.secrets.M2M_DOMAIN,
		clientId: event.secrets.M2M_CLIENT_ID,
		clientSecret: event.secrets.M2M_CLIENT_SECRET,
		scope: "update:users"
	});

	managementClientInstance.users.assignRoles(
		{ id: event.user.user_id }, 
		{ roles: ["ROLE_ID_TO_ADD"]}, 
		(error, user) => {
			if (error) {
				return api.access.deny(error.message);
			}

			// ... additional code
		}
	);
};

Was this helpful?

/

リダイレクトを変換する

ルールは、外部ページにログインしているユーザーをリダイレクトし、応答を待つことができます。この場合、リダイレクト前のすべてのルールが2回実行されます。つまり、リダイレクト前に1回と、応答時に1回です。リダイレクトと応答のロジックは通常、同じルールに含まれます。

アクションでは、リダイレクトが発生するとアクションパイプラインが一時停止し、ユーザーが戻ると再開されます。また、エクスポートされたリダイレクトトリガー関数はリダイレクトコールバックとは別のものです。

変更前

function myRulesFunction(user, context, callback) {
    if (context.protocol === "redirect-callback") {
        // User was redirected to the /continue endpoint
        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"
    ) {
        // User cannot be redirected
        return callback(null, user, context);
    }
    // User is logging in directly
    if (!user.app_metadata.wasRedirected) {
        context.redirect = {
            url: "https://example.com",
        };
        callback(null, user, context);
    }
}

Was this helpful?

/

変換後

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);
};

Was this helpful?

/

現在のSSOクライアント参照を変換します

ルールのcontext.ssoオブジェクトは、現在のセッションとそれを使用しているクライアントに関する詳細を提供します。詳細については、ルールのコンテキストオブジェクトプロパティcontext.ssoエントリを参照してください。同様の情報は、アクションのevent.sessionオブジェクトでも入手できます。

変更前

function (user, context, callback) {

  const clients = context.sso?.current_clients ?? []; 

  if (clients.length > 0) { 
	context.idToken.clients = clients.join(" "); 
  }

  return callback(null, user, context);
}

Was this helpful?

/

変換後

exports.onExecutePostLogin = async (event, api) => {
  const clients = event?.session?.clients ?? []; 

  if (clients.length > 0) { 
    api.idToken.setCustomClaim('clients', clients.map(c=> c?.client_id).join(" ")); 
  }
};

Was this helpful?

/

移行を完了する

新しいアクションコードを作成してテストしたら、アクションを有効にし、ルールを無効にする必要があります。これらの2つのタスクは連続してすばやく実行できますが、順序によっては、両方が実行されない、またはどちらも実行されない時間が短期間発生する場合があります。アクティブなルールは導入されたアクションの前に実行されるため、ルールパイプラインの最後から開始して逆方向に作業すると、アクションで他のロジックを構築およびテストするときに、一部のロジックをルールに保持したままにすることができます。