示例:常见使用场景¶
以下是一些使用 Hook 在身份验证流程中为 Authany 添加操作的示例。这样可以扩展安全功能,例如更精细的访问控制、自适应 MFA 和可变速率限制。
在 Authany 管理台进入高级 > Hook。然后在阻塞事件部分点击新增,选择 “TypeScript”,再点击“编辑脚本”来编写将在所需事件上执行的代码。
如果您想在自己的服务器上运行代码,也可以使用 Webhook。详情请参阅 Webhook。
仅允许公司内网发起注册¶
您可以使用 user.pre_create 事件,只允许来自特定 IP 的用户注册。
例如,仅当用户位于网络 123.45.67.89 中时才允许注册:
export default async function (
e: EventUserPreCreate
): Promise<HookResponse> {
const allowedIp = "123.45.67.89"; // The IP address of the corporate network
if (e.context.ip_address == allowedIp) {
return {
is_allowed: true,
};
} else {
return {
is_allowed: false,
reason: "You are not allow to sign up to this organisation",
title: "Sign-up not allowed!",
};
}
}
允许特定地理位置访问¶
您可以使用 authentication.post_identified 事件,屏蔽或允许来自特定位置的用户。
例如,仅允许来自香港的注册和登录:
export default async function (
e: EventAuthenticationPostIdentified
): Promise<EventAuthenticationPostIdentifiedHookResponse> {
if (e.context.geo_location_code === "HK") {
return {
is_allowed: true,
};
} else {
return {
is_allowed: false,
};
}
}
根据用户角色控制特定应用的访问权限¶
您可以使用 authentication.post_identified,根据用户的角色、标准属性或自定义属性,控制其能否在特定应用中完成身份验证。
例如,如果您希望只有角色为 sales 的用户才能访问客户端 ID 为 c8da9b322e1f494e 的应用 crm-system:
export default async function (
e: EventAuthenticationPostIdentified
): Promise<EventAuthenticationPostIdentifiedHookResponse> {
if (
e.context.client_id === "c8da9b322e1f494e" &&
e.payload.authentication_context?.authentication_flow?.type != null &&
["login", "signup"].includes(e.payload.authentication_context?.authentication_flow?.type)
) {
const user = e.payload.authentication_context.user
if (user?.roles?.includes("sales")) {
return {
is_allowed: true,
};
} else if (user?.custom_attributes?.can_access_crm === "true") {
// Alternatively, use custom_attributes to determine if the user is allowed to access the app
return {
is_allowed: true,
};
} else {
return {
is_allowed: false,
reason: "You don't have permission to use this app"
};
}
}
// Allow login or signups of other clients
return {
is_allowed: true,
};
}
根据邮箱域名控制访问权限¶
您可以使用 authentication.post_identified,阻止未使用指定邮箱域名的用户在您的系统中注册。
例如,您只想允许邮箱域名为 @authgear.com 的用户注册:
export default async function (
e: EventAuthenticationPostIdentified
): Promise<EventAuthenticationPostIdentifiedHookResponse> {
const email = e.payload.identification.identity?.claims?.email
if (typeof email === "string" && email.endsWith("@authgear.com")) {
return {
is_allowed: true,
};
}
// Block signup of all other emails
return {
is_allowed: false,
};
}
屏蔽周末登录¶
您可以使用 authentication.post_identified,阻止用户在周末登录。
例如,如果您的业务只在工作日运营,因此不希望任何用户在周末登录:
export default async function (
e: EventAuthenticationPostIdentified
): Promise<EventAuthenticationPostIdentifiedHookResponse> {
const today = new Date();
// 0 is sunday, and 6 is saturday
if (today.getDay() === 0 || today.getDay() === 6) {
return {
is_allowed: false,
};
}
return {
is_allowed: true,
};
}
注意:即使周末登录被屏蔽,工作日签发的刷新令牌和访问令牌也不会失效。
根据身份验证方式屏蔽用户登录¶
您可以使用 authentication.pre_authenticated,根据身份验证过程中使用的 AMR(身份验证方法参考) 屏蔽用户登录。
例如,如果您想屏蔽在身份验证过程中未使用 mfa(多重身份验证)的用户:
export default async function (
e: EventAuthenticationPreAuthenticated
): Promise<EventAuthenticationPreAuthenticatedHookResponse> {
if (!e.payload.authentication_context.amr?.includes("mfa")) {
return {
is_allowed: false,
};
}
return {
is_allowed: true,
};
}
在特定条件下启用机器人防护¶
使用 authentication.pre_initialize,在特定条件下启用机器人防护。
例如,如果您只想为香港以外的用户显示验证码:
首先,在管理台中启用机器人防护,并将验证要求设置为 “Never”。
然后在您的 authentication.pre_initialize Hook 中返回 bot_protection.mode:
export default async function (
e: EventAuthenticationPreInitialize
): Promise<EventAuthenticationPreInitializeHookResponse> {
if (e.context.geo_location_code !== "HK") {
return {
is_allowed: true,
bot_protection: {
mode: "always",
},
};
}
// Else, simply allow the login
return {
is_allowed: true,
};
}
这会覆盖配置中 bot_protection 原有的 mode 值。因此,当用户尝试在香港以外的地区注册或登录时,bot_protection 将会启用。
自适应 MFA:仅对高风险用户要求 MFA¶
您可以使用 authentication.pre_authenticated 来实现自适应 MFA。
例如,您认为来自 HK 以外的登录风险更高,因此应当要求 MFA:
export default async function (
e: EventAuthenticationPreAuthenticated
): Promise<EventAuthenticationPreAuthenticatedHookResponse> {
if (e.context.geo_location_code !== "HK") {
return {
// Allow the login with a mfa contraint
is_allowed: true,
constraints: {
amr: ["mfa"],
},
};
}
// Else, simply allow the login
return {
is_allowed: true,
};
}
如果响应中返回的 constraints.amr 值为 ["mfa"],则根据身份验证流程类型的不同:
- 注册 / 升级:如果用户在流程中没有设置任何次要身份验证器,系统会在流程末尾添加一个步骤,强制用户设置次要身份验证器。
- 登录 / 重新验证:如果用户在流程中没有使用任何次要身份验证器,系统会在流程末尾添加一个双重验证步骤。如果用户此前从未设置过双重验证,该流程会失败。
- 账号找回:无影响,因为账号找回不支持双重验证。
了解更多关于 AMR 的信息,请参阅 AMR(身份验证方法参考)。
进阶:为特定 IP 范围的身份验证应用更严格的速率限制¶
您可以使用 Hook,为特定 IP 范围的身份验证流程应用更严格的速率限制。
例如,您希望将账号枚举限制为来自数据中心 IP 地址的请求每分钟 5 次,其他请求每分钟 10 次。
步骤 1:调整基础速率限制
首先,在项目配置中添加以下速率限制配置。(管理台 > 高级 > 编辑配置)
这会将账号枚举的基础速率限制设置为每分钟 10 次。
步骤 2:在 Hook 中通过权重覆盖速率限制
然后,在 authentication.pre_initialize 事件中创建以下 Hook:
function ipToBinary(ip: string): string {
return ip
.split(".")
.map((octet) => parseInt(octet, 10).toString(2).padStart(8, "0"))
.join("");
}
function cidrToPrefix(cidr: string): string {
const [ip, lengthStr] = cidr.split("/");
const prefixLength = parseInt(lengthStr, 10);
const ipBinary = ipToBinary(ip);
return ipBinary.substring(0, prefixLength);
}
export default async function (
e: EventAuthenticationPreInitialize
): Promise<EventAuthenticationPreInitializeHookResponse> {
if (e.context.ip_address == null) {
// ip unknown, block it.
return {
is_allowed: false,
};
}
const ipBinary = ipToBinary(e.context.ip_address);
// ip ranges from https://www.gstatic.com/ipranges/cloud.json
const dataCenterIPRanges = ["34.125.0.0/16", "34.124.24.0/21"];
// 34.125.0.0/16
if (dataCenterIPRanges.some((cidr) => ipBinary.startsWith(cidrToPrefix(cidr)))) {
return {
is_allowed: true,
rate_limits: {
"authentication.account_enumeration": {
weight: 2,
},
},
};
} else {
return {
is_allowed: true,
};
}
}
将 "rate_limits.authentication.account_enumeration.weight" 设置为 2 后,每次账号枚举尝试都会占用速率限制中的 2 次配额。因此,每分钟只允许 5 次尝试。(10 / 2 = 5)。了解更多请参阅覆盖速率限制