Auth0 Hooks完成了Auth0平台中规则的定制能力。然而,Auth0 Actions提供了一个统一的、强大的环境来执行你可以用规则和钩子完成的相同任务。通过这篇文章了解如何将你现有的Auth0 Hooks迁移到Auth0 Actions。
Auth0 Actions和自定义
Auth0最引人注目的功能之一是它对自定义的支持。除了你的应用程序的基本登录和注销功能外,你可以调整Auth0的一些幕后流程的标准流程,以满足你的特定需求。例如,你可以在用户注册你的应用程序后进行额外的认证检查或执行特定的操作。
许多Auth0客户熟悉的进行这些定制的功能是Auth0规则和Auth0 Hooks。Auth0规则允许你定制认证流程,而Auth0钩子使你能够为一些可扩展的点定制Auth0行为,如用户注册前后、密码更改、电话信息发送,等等。
现在你有了Auth0 Actions,这个Auth0功能允许你实现与Rules和Hooks一样的定制目标。Auth0 Actions为你提供了一个统一的开发环境,以定制你习惯于用规则和钩子做的可扩展性点。Actions不仅为你提供了与Rules和Hooks相同的功能。它们还为你提供了一些额外的功能,简化了你的开发经验。
- Actions为你提供了一个集成的版本系统。你可以修改一个Action而不影响生产中的Action,创建多个版本,并恢复以前的版本。
- Actions编辑器支持代码提示、所有的npm包,以及隔离的秘密管理。
- 你可以利用Auth0市场上越来越多的无代码Action集成,这些集成可以解决常见的身份用例,如同意管理、渐进式剖析和用户验证。这些集成由合作伙伴构建,使开发人员能够更快地进入市场,同时减少与自定义代码相关的开发和维护成本。
虽然Auth0 Actions给你带来了一个改进的定制环境,但规则和钩子仍然可用,而且它们都可以一起使用。记住,规则和钩子在Actions之前执行。
Auth0 Actions是Auth0规则和Hooks的一个推荐替代方案。如果你用规则和钩子定制了Auth0流程,现在想迁移到Auth0 Actions,你就来对地方了。
这篇文章提供了一些将Auth0 Hooks迁移到Auth0 Actions的指南。请看这篇文章,将你的Auth0规则迁移到Auth0动作。
代码转换指南
要将你的Auth0 Hooks转换为同等的Auth0 Action,请遵循本节中的指南。
本文假设你对如何创建Hook和如何创建Action有基本了解。如果没有,请参考Auth0关于Hooks创建和Action编写的文档。在这两种情况下,你都需要一个Auth0账户。如果你没有,你可以免费获得一个。
将钩子类型映射到动作触发器
你需要做的第一件事是确定与你的Hook类型相对应的Action Trigger。Auth0动作触发器是发生在Auth0流程中的事件。下表将每个Hook类型映射到相应的Action Trigger。
钩子类型 | 行动触发器 |
---|---|
用户注册前 | 用户注册前 |
用户注册后 | 用户注册后 |
更改密码后 | 更改密码后 |
发送电话信息 | 发送电话信息 |
客户凭证交换 | M2M/客户凭证 |
钩子类型和动作触发器的名称几乎是相同的。所以,当你要把你的Hook转换成Action时,你需要从列表中选择正确的Trigger来创建Action,如下图所示。
当你选择一个Trigger时,你会在Action编辑器中得到一个空的命名出口。具体导出的名称取决于所选的触发器。例如,如果你把你的Post-User Registration Hook转换成Post User Registration Action Trigger,你会在Action编辑器中得到以下代码。
exports.onExecutePostUserRegistration = async (event) => {
};
你可以在下表中看到你的Hook类型和相应的导出名称之间的这种映射关系。
钩子类型 | 动作触发器 | 命名为出口的动作 |
---|---|---|
用户注册前 | 用户注册前 | onExecutePreUserRegistration |
用户注册后 | 用户注册后 | onExecutePostUserRegistration |
更改密码后 | 更改密码后 | onExecutePostChangePassword |
发送电话信息 | 发送电话信息 | onExecuteSendPhoneMessage |
客户凭证交换 | M2M/客户凭证 | onExecuteCredentialsExchange |
你的Hook的主体将被放入你新创建的Action的主体中,你将随着你的学习进行一些改变。
映射参数
Hooks函数根据Hook的类型,在多个参数中接收关于用户、客户端、请求等的上下文数据。大多数的Hook函数类型都有三个参数。user
,context
, 和cb
。其他Hook函数类型有更多的参数来接收额外的数据。
行动将运行环境传递给行动函数的数据整合为不超过两个参数:event
和api
。Hooks的user
和context
对象的所有只读属性现在都在Actions的event
对象中。任何与运行环境的交互,比如添加用户元数据,都是通过api
对象的方法实现的。
在保持相同名称的同时,event
和api
对象的结构根据特定的Action Trigger而改变。下表将Hook类型映射到event
和api
对象的具体文档中。
钩子类型 | 名为出口的行动 | event docs | api docs |
---|---|---|---|
用户注册前 | onExecutePreUserRegistration | event docs | api docs |
用户注册后 | onExecutePostUserRegistration | event 文档 | |
更改密码后 | onExecutePostChangePassword | event 文档 | |
发送电话信息 | onExecuteSendPhoneMessage | event 文档 | |
客户端凭证交换 | onExecuteCredentialsExchange | event 文档 | api docs |
处理依赖性问题
Auth0 Hooks允许你包含来自成千上万的npm包的依赖关系,但并不是所有的npm注册表中的包都可用。如果你需要一个Hooks无法使用的包(甚至只是一个npm包的特定版本),你需要向Auth0申请。
在Actions中,你可以添加npm注册表中所有可用软件包的依赖关系,这意味着超过一百万个。
另外,在Actions中,你不需要像Hooks那样在代码中指定包的版本。例如,在你的Hook中,你必须这样声明一个依赖关系。
// Hook for Post-User Registration extensibility point
module.exports = function (user, context, cb) {
const axios = require("axios@0.22.0");
// ... other code
}
在Actions中,你只需在编辑器的模块管理器中指定包的版本,而你的代码看起来就像下面这样。
const axios = require("axios");
exports.onExecutePostUserRegistration = async (event) => {
// ... other code
};
注意,你在函数主体之外导入了依赖关系,更像是一个Node.js模块的风格。
转换Hooks的回调
在客户端凭证交换和用户注册前的钩子中,你使用cb
参数来传回你的处理结果。例如,当你的代码完成了它的步骤,它需要将控制权传递给运行时环境,而运行时环境可能会运行下一个Hook。在这种情况下,你应该调用 cb()
函数,传递处理结果,如果处理失败则传递一个错误实例。
Actions则更直接。你不需要调用任何回调函数来传递控制给运行环境。只要使用 return
来代替。
你可以与运行时环境互动,通过传递给Action Trigger的api
参数。例如,M2M/Client-Credentials和Pre User Registration动作触发器有api
对象作为第二个参数。在这些动作触发器中,你可以通过调用 api.access.deny()
方法来防止用户注册。
看一下下一节,看看这一准则的实际应用。
转换秘密
与Hooks类似,你可以使用Action编辑器中的Add Secret按钮为Action存储秘密,并指定一个键和一个值。
在Action函数中访问秘密与Hooks略有不同。
在你的Hook函数中,你可以像下面的例子那样访问秘密。
module.exports = function (user, context, cb) {
const mySecret = context.webtask.secrets.MY_SECRET;
// ... other code
}
在一个Action Trigger函数中,你获得秘密的方式如下。
exports.onExecutePostChangePassword = async (event) => {
const mySecret = event.secrets.MY_SECRET;
// ... other code
};
通过动作编辑器定义的秘密可以作为对象的属性。 event.secrets
对象的属性。
转换实例
一旦你学会了将你的Hooks转换为相应的Action Triggers的一般准则,让我们通过几个转换例子来实践它们。
为新用户添加元数据
考虑一下下面这个用户注册前钩子的代码。
module.exports = function (user, context, cb) {
const response = {};
if (user.phoneNumber) {
response.user = {
user_metadata: { favorite_color: "green" }
};
cb(null, response);
} else {
return cb(new Error("Phone number is mandatory!"));
}
};
只有在用户提供电话号码的情况下,这段代码才将绿色作为用户最喜欢的颜色分配给用户的元数据。否则,它会引发一个错误并阻止用户注册。
相应的用户注册前动作触发器看起来如下。
exports.onExecutePreUserRegistration = async (event, api) => {
if (user.phoneNumber) {
api.user.setUserMetadata("favorite_color", "green");
} else {
return api.access.deny("Phone number is mandatory!");
}
};
而不是调用 cb()
回调函数来把新创建的元数据传回给运行时环境,而是使用该 api.user.setUserMetadata()
方法来实现同样的目标。它再次使用api
对象来防止用户的注册,而不是使用 cb()
函数。
添加自定义索赔
在客户凭证交换Hook中,你可以为访问令牌添加一个自定义索赔,如下例所示。
module.exports = function(client, scope, audience, context, cb) {
const access_token = {};
const namespace = "https://myapp.example.com";
access_token.scope = scope;
access_token[`${namespace}/favorite_color`] = "green";
cb(null, access_token);
};
你可以把这个Hook功能变成一个M2M/客户凭证动作触发器,如下所示。
exports.onExecuteCredentialsExchange = async (event, api) => {
const namespace = "https://myapp.example.com";
api.accessToken.setCustomClaim(`${namespace}/favorite_color`, "green");
};
同样,你不需要调用任何回调函数。你只需使用 api.accessToken.setCustomClaim()
方法来给新创建的自定义声明分配一个值。
发送通知邮件
让我们考虑一下下面的更改密码后Hook。它通过使用SendGrid在密码更改时向用户发送一封电子邮件。
module.exports = function (user, context, cb) {
const request = require("request");
const sendgridApiKey = context.webtask.secrets.SENDGRID_API_KEY;
request.post({
url: "https://api.sendgrid.com/v3/mail/send",
headers: {
"Authorization": "Bearer " + sendgridApiKey
},
json: {
personalizations: [{
to: [{
email: user.email
}]
}],
from: {
email: "admin@example.com"
},
subject: "Your password was changed",
content: [{
type: 'text/plain',
value: `The password for your ${context.connection.name} account ${user.email} was recently changed.`
}]
}
}, function (err, resp, body) {
if (err || resp.statusCode !== 202) {
return cb(err || new Error(body.errors[0].message));
}
cb();
});
};
这个Hook可以转换为下面的Post Change Password Action Trigger。
exports.onExecutePostChangePassword = async (event) => {
const request = require("request");
//👇 changed code
const sendgridApiKey = event.secrets.SENDGRID_API_KEY;
request.post({
url: "https://api.sendgrid.com/v3/mail/send",
headers: {
"Authorization": "Bearer " + sendgridApiKey
},
json: {
personalizations: [{
to: [{
email: user.email
}]
}],
from: {
email: "admin@example.com"
},
subject: "Your password was changed",
content: [{
type: "text/plain",
//👇 changed code
value: `The password for your ${event.connection.name} account ${event.user.email} was recently changed.`
}]
}
}, function (err, resp, body) {
if (err || resp.statusCode !== 202) {
//👇 changed code
throw (err || new Error(body.errors[0].message));
}
//👇 changed code
//cb();
});
};
看一下突出显示的语句。正如你所看到的,这些是对Hook函数主体的唯一改变。你适应了。
- 获取SendGrid API秘密的方式。
- 获取连接和用户数据的方式。
- 在失败的情况下如何从行动中返回。
也注意到最后一行与回调有关的注释 cb()
调用的最后一行注释:在你的Action中不再需要它。
规划你的迁移策略
读完这篇文章后,你对Hooks和Actions中使用的代码的主要区别有了更好的理解。当把Hooks转换为Action时,除了执行转换外,还有更多需要考虑的问题。你还需要计划一个策略,将你的代码从Hooks迁移到Actions,以避免生产环境中出现问题。
在这方面,你应该建立一个暂存环境,测试从Hooks转换而来的Action。这可以帮助你确保你的Action的代码按预期运行。
此外,考虑到你可以在租户中同时拥有Hooks和Action。当两者都处于活动状态时,Actions会在Hooks之后运行。因此,你可以通过激活相应的Action,然后停用Hook,一次迁移一个Hook。这有助于将破坏你的生产环境的风险降到最低,尽管仍然存在一些注意事项。
总结
在这篇文章中,你了解到Auth0 Actions是定制标准Auth0流程的新的统一方式。如果你习惯于创建Auth0 Hooks来满足你的定制需求,现在你可以利用Actions来增强你的开发经验。这篇文章解释了将你现有的Hooks转换为新的Actions的一些准则,并为你提供了一些转换实例。
当从Hooks转移到Actions时,不要忘记准确地计划你的迁移。查看文档以了解更多关于Hooks到Actions的迁移过程。