Client Credentials Exchange 拡張ポイントでは、Hooks を使用して、Client Credentials Flow により Authentication API の POST /oauth/token endpoint から が発行される際に、カスタム処理を実行できます。たとえば、トークンの発行を拒否したり、アクセストークンにカスタムクレームを追加したり、スコープを変更したりできます。詳細については、Client Credentials Flow を参照してください。
この拡張ポイントの Hooks はブロッキング (同期) です。つまり、トリガー処理の一部として実行され、Hook が完了するまで Auth0 パイプラインの残りの処理は実行されません。
Client Credentials Exchange 拡張ポイントの triggerId は credentials-exchange です。この拡張ポイント用の Hooks の作成方法については、Create Hooks を参照してください。
他の拡張ポイントについては、Extensibility Points を参照してください。
Client Credentials Exchange の拡張ポイントで実行される Hook を作成する際には、以下のスターターコードが参考になります。Hook 関数に渡して使用できるパラメーターは、コードサンプルの冒頭に記載されています。
/**
@param {object} client - クライアント情報
@param {string} client.name - クライアント名
@param {string} client.id - クライアントID
@param {string} client.tenant - Auth0 テナント名
@param {object} client.metadata - クライアントメタデータ
@param {array|undefined} scope - トークンのスコープクレームを表す文字列の配列、またはundefined
@param {string} audience - トークンのオーディエンスクレーム
@param {object} context - Auth0 コンテキスト情報
@param {object} context.webtask - Hook (webtask) コンテキスト
@param {function} cb - function (error, accessTokenClaims)
*/
module.exports = function(client, scope, audience, context, cb) {
var access_token = {};
access_token.scope = scope; // この行は削除しないでください
// スコープを変更するか、追加のクレームを付与する
// access_token['https://example.com/claim'] = 'bar';
// access_token.scope.push('extra');
// トークンを拒否し、OAuth2 エラーレスポンスを返す
// if (denyExchange) {
// // HTTP 400 を返す場合: { "error": "invalid_scope", "error_description": "Not authorized for this scope." }
// return cb(new InvalidScopeError('Not authorized for this scope.'));
//
// // HTTP 400 を返す場合: { "error": "invalid_request", "error_description": "Not a valid request." }
// return cb(new InvalidRequestError('Not a valid request.'));
//
// // HTTP 500 を返す場合: { "error": "server_error", "error_description": "A server error occurred." }
// return cb(new ServerError('A server error occurred.'));
// }
cb(null, access_token);
};
次の点に注意してください。
-
サンプルコードの末尾にあるコールバック関数 (
cb) は処理完了を示すためのもので、必ず含める必要があります。
-
access_token.scope = scope の行により、付与されたすべてのスコープがアクセストークンに含まれるようになります。これを削除すると、すべてのスコープがリセットされ、トークンにはスクリプトで追加したスコープのみが含まれます。
Client Credentials Exchange 拡張ポイントで Hook を実行すると、デフォルトのレスポンスオブジェクトは次のとおりです。
{
"scope": "array of strings"
}
スコープと追加のクレームを反映するようにスターターコードをカスタマイズしたら、Hook Editor に組み込まれているランナーを使って Hook をテストできます。このランナーは、Client Credentials Exchange で取得されるものと同じボディとレスポンスで Hook 呼び出しをシミュレートします。
ランナーを使ってコードを実行するには、先に保存する必要があります。保存すると、元のコードは上書きされます。
スターターコードに基づく Hook を実行すると、レスポンスオブジェクトは次のようになります。
{
"audience": "https://my-tenant.auth0.com/api/v2/",
"client": {
"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"name": "client-name",
"tenant": "my-tenant",
"metadata": {
"plan": "full"
}
},
"scope": [
"read:connections"
]
}
サンプルスクリプト: アクセストークンに追加のスコープを加える
この例では、Hook を使用して、アクセストークンにすでに含まれているスコープに追加のスコープを加えます。
module.exports = function(client, scope, audience, context, cb) {
// 追加するスコープ
var access_token = {};
// アクセストークンに現在設定されているスコープを取得し、
// 処理対象のオブジェクトに追加する
// この行は削除しないこと!
access_token.scope = scope;
// `read:resource` スコープを追加する
access_token.scope.push('read:resource');
// 処理完了を通知し、新しいスコープの配列を返すコールバック
cb(null, access_token);
};
詳しくは、スコープを参照してください。
この Hook を実行したときのレスポンスオブジェクトは、次のとおりです。
{
"scope": [
"read:connections",
"read:resource"
]
}
サンプルスクリプト: アクセストークンにクレームを追加する
この例では、名前空間付きのカスタムクレームとその値をアクセストークンに追加します。詳細については、名前空間付きカスタムクレームの作成を参照してください。
発行されたトークンには、次の項目をクレームとして追加できます。
- レスポンスオブジェクトの
scope プロパティ
- 名前空間付きのプロパティ名を持つ任意のプロパティ
この拡張ポイントでは、レスポンスオブジェクトのその他すべてのプロパティは無視されます。
フック内から設定済みの Hook Secret にアクセスするには、context.webtask.secrets.SECRET_NAME を使用します。
module.exports = function(client, scope, audience, context, cb) {
// 追加するクレーム
var access_token = {};
// トークンに追加する新しいクレーム
access_token['https://example.com/foo'] = 'bar';
// 完了を示し、新しいクレームを返すコールバック
cb(null, access_token);
};
この Hook を実行すると、レスポンスオブジェクトは次のようになります。
{
"https://example.com/foo": "bar"
}
サンプルスクリプト: エラーを発生させる、またはアクセストークンを拒否する
この例では、カスタム Error オブジェクトを使用して OAuth2 のエラーレスポンスを生成します。 (詳細については、IETF Datatracker の OAuth2 RFC - Section 5.2を参照してください。)
次のように、通常の JavaScript エラーがコールバックで返された場合:
module.exports = function(client, scope, audience, context, cb) {
// 完了を示し、新しいクレームを返すコールバック
cb(new Error("Unknown error occurred.");
};
次に、/oauth/token エンドポイントに client_credentials グラントをリクエストすると、Auth0 は次のように応答します。
HTTP 500
{ "error": "server_error", "error_description": "Unknown error occurred." }
ただし、OAuth2 エラーレスポンスをより細かく制御したい場合は、代わりに 3 つのカスタム Error オブジェクトを使用できます。
module.exports = function(client, scope, audience, context, cb) {
const invalidScope = ...; // スコープが有効かどうかを確認する
if(invalidScope) {
cb(new InvalidScopeError("Scope is not permitted."));
}
};
次に、/oauth/token エンドポイントに client_credentials グラントをリクエストすると、Auth0 は次のように応答します:
HTTP 400
{ "error": "invalid_scope", "error_description": "Scope is not permitted." }
module.exports = function(client, scope, audience, context, cb) {
const invalidRequest = ...; // リクエストが有効かどうかを判定する
if(invalidRequest) {
cb(new InvalidRequestError("Bad request."));
}
};
その後、/oauth/token エンドポイントに client_credentials グラントをリクエストすると、Auth0 は次のように応答します。
HTTP 400
{ "error": "invalid_request", "error_description": "Bad request." }
module.exports = function(client, scope, audience, context, cb) {
callOtherService(function(err, response) {
if(err) {
return cb(new ServerError("Error calling remote system: " + err.message));
}
});
};
その後、/oauth/token エンドポイントに client_credentials グラントを要求すると、Auth0 から次の応答が返されます。
HTTP 400
{ "error": "server_error", "error_description": "Error calling remote system: ..." }
現時点では、組み込みの JavaScript Error クラスと ServerError の動作は同じですが、ServerError クラスを使用すると、返される OAuth2 エラーを明示的に指定できます。