您应该将自定义 REST 端点添加到 Keycloak,以便能够从您的自定义 UI 启用 2FA。我们以前做过。它并没有那么复杂,但它需要您查看 Keycloak 源代码,以了解激活 OTP 时它在做什么。要检查/使用的一些重要类是 TotpBean、OTPCredentialModel 和 OTPPolicy。
为了启用 2FA,我们需要在自定义 UI 中显示 QR 码图像。因此,我们向 Keycloak 添加了一个端点,该端点实例化了一个 TotpBean 的实例。它使您可以访问 QR 码图像和生成图像的等效字符串表示所需的秘密值,以便可以在 2FA 应用程序(例如 Google Authenticator)中对其进行扫描/输入。以下是此类端点的示例:
@GET
@Produces({MediaType.APPLICATION_JSON})
@Path("/o2p-enable-config/{email}")
@NoCache
public Response fetchOtpEnableConfig(@Email @PathParam("email") String email) {
UserModel user = session.users().getUserByEmail(email, realm);
TotpBean totp = new TotpBean(session, realm, user, session.getContext().getUri().getRequestUriBuilder());
return Response
.ok(new YouOTPResponseClass("data:image/png;base64, " + totp.getTotpSecretQrCode(), totp.getTotpSecret(), totp.getTotpSecretEncoded()))
.build();
}
然后在您自己的后端,您调用此端点并将用户的电子邮件发送给它并接收图像和秘密值。您可以在 UI 中按原样显示图像,并将秘密值保留在后端(例如,在用户会话中)。当用户使用应用程序扫描图像并在您的自定义 UI 中输入应用程序提供的 totp 值时,您会将 totp 值和密钥发送到您应该添加到 Keycloak 的另一个端点。第二个端点是对值进行验证并启用 2FA 的端点。
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Path("/enable-2fa/{email}")
@NoCache
public Response enable2Fa(@Email @PathParam("email") String email, OtpDetails optDetails) {
OTPPolicy policy = realm.getOTPPolicy();
String totp = optDetails.getTotp();
UserModel user = session.users().getUserByEmail(email, realm);
OTPCredentialModel credential = OTPCredentialModel.createFromPolicy(realm, optDetails.getSecret(), optDetails.getUserLabel());
if (CredentialValidation.validOTP(totp, credential, policy.getLookAheadWindow())) {
CredentialHelper.createOTPCredential(session, realm, user, totp, credential);
return Response.noContent().status(204).build();
} else {
return Response.status(BAD_REQUEST).build();
}
}
Keycloak 支持每个用户的多个 2FA。这就是为什么它还有一个名为label 的属性,允许用户命名它们,以便在2FA 登录场景中显示给定名称。您还可以允许用户在您的自定义 UI 中输入标签值并将其传递给第二个端点(或者如果您不允许您的用户设置多个 2FA,则只需将一个空值传递给 Keycloak)。
我知道这看起来很复杂,但实际上并没有那么复杂。 Keycloak 域模型设计得很好,当您熟悉它时,您可以轻松找到您需要做的事情并将其包装在自定义 API 中。 但始终确保公开功能不会损害系统的整体安全模型。