Keycloak - 多重/2FA 因素 - OTP - 二维码 - 自定义登录屏幕 - Rest API

Ank*_*hal 8 authentication user-registration keycloak keycloak-services multi-factor-authentication

我有自己的登录页面,用户在其中输入用户名/密码。 该用户名/密码用于通过 Keycloak Rest API 登录。

http://localhost:8080/auth/realms/Demo/protocol/openid-connect/token

input - {username,password,grant_type,client_secret,client_id}
Run Code Online (Sandbox Code Playgroud)

我得到的回应是access token

现在我希望启用身份验证器 ( Google Authenticator)。我已经从后端启用了它。现在,如果用户希望通过我的应用程序登录,我的登录页面需要获取以下详细信息。

1.)不知何故,我需要包含在用户名/密码验证后出现在keycloak登录页面上的二维码,以便在用户输入用户名/密码后首次登录时显示在我的登录屏幕上。那么我们是否有任何 API 可以返回 Keycloak 二维码图像作为响应。

2.) 后续登录将有 OTP 字段,因此需要一个 REST api 来传递 OTP 以及用户名/密码。

如果 keycloak 有 REST API,请提供帮助。通过 Javascript 集成。

与此处用例 1 中描述的流程类似

只是想使用keycloak作为数据库,为我做所有操作,输入将是我的屏幕。我确实希望在登录时重定向 URL,但应该独立部署。

Tho*_*bbe 13

我已经成功地通过 Keycloak 的其余 API 实现了这一点。为了实现这一点,您需要使用 SPI 自己扩展 Keycloak。为此,创建您自己的 Java 项目并扩展org.keycloak.services.resource.RealmResourceProviderorg.keycloak.services.resource.RealmResourceProviderFactory. 您可以在官方文档 ( https://www.keycloak.org/docs/latest/server_development/#_extensions)、github示例和其他堆栈溢出帖子中找到如何执行此操作的更多信息。

一旦你启动并运行它,你就可以像这样实现它:

@GET
@Path("your-end-point-to-fetch-the-qr")
@Produces({MediaType.APPLICATION_JSON})
public YourDtoWithSecretAndQr get2FASetup(@PathParam("username") final String username) {
    final RealmModel realm = this.session.getContext().getRealm();
    final UserModel user = this.session.users().getUserByUsername(username, realm);
    final String totpSecret = HmacOTP.generateSecret(20);
    final String totpSecretQrCode = TotpUtils.qrCode(totpSecret, realm, user);
    return new YourDtoWithSecretAndQr(totpSecret, totpSecretQrCode);
}

@POST
@Path("your-end-point-to-setup-2fa")
@Consumes("application/json")
public void setup2FA(@PathParam("username") final String username, final YourDtoWithData dto) {
    final RealmModel realm = this.session.getContext().getRealm();
    final UserModel user = this.session.users().getUserByUsername(username, realm);
    final OTPCredentialModel otpCredentialModel = OTPCredentialModel.createFromPolicy(realm, dto.getSecret(), dto.getDeviceName());
    CredentialHelper.createOTPCredential(this.session, realm, user, dto.getInitialCode(), otpCredentialModel);
}
Run Code Online (Sandbox Code Playgroud)

通过 GET 收到的机密必须通过 POST 发回。初始代码是您的 2FA 应用程序(例如 Google Authenticator)中的代码。二维码是一个字符串,可以显示在imgsrc 中'data:image/png;base64,' + qrCodeString;


cod*_*ips 10

我知道这是一个老问题,但我最近一直在研究类似的问题,因此认为与可能正在研究这个问题并想知道可能性是什么的其他人分享我的发现可能是有价值的。


TL;DR
您只能真正使用现有的 Keycloak 操作来执行此操作,或将 https://{keycloak 服务器 URL}/auth/realms/{realm name}/account 中找到的用户帐户管理页面嵌入 iframe 中。恐怕就是这样了。在我看来,目前最好的做法是直接将操作分配给帐户或使用凭据重置电子邮件来分配操作;如果需要,这两个操作都可以通过 Admin API 完成:

发送包含指定操作的凭据重置电子邮件:
https://www.keycloak.org/docs-api/11.0/rest-api/index.html#_executeactionsemail

直接在帐户上设置操作(包括您在正文中发送到端点的用户 JSON 的 requiredActions 部分中的操作):
https ://www.keycloak.org/docs-api/11.0/rest-api/index .html#_updateuser


背景是,作为我一直在从事的一个项目的一部分,我们想看看是否可以有一种集成的方式,让用户在为用户创建新帐户时设置其初始密码和 OTP 设备,因为默认方法使用“凭据重置”功能从 Keycloak 向他们发送电子邮件有以下限制:a) 它不提供指向应用程序本身的链接,除非您覆盖主题,并且如果您有针对不同用户的多个应用程序实例,无法知道要为哪个实例提供链接,因此可能最终必须包含它们的列表,并且b)即使对主题进行了更改,它通常也感觉不是真正的应用程序原生。如果您明智的话,我建议您停止使用此功能 - 请参阅上面的 TL;DR 部分了解详细信息。

因此,简而言之,没有用于接收 QR 码来设置 OTP 设备的 API 端点。但是,可以从两个位置检索 QR 码:当您以已分配给其帐户的“配置 OTP”操作的用户身份登录时的 OTP 设备设置屏幕,以及用户自己的帐户管理屏幕。

配置 OTP 操作屏幕的第一个选项是不可能的。它仅在您登录时显示,因此根据定义,用户必须通过 Keycloak 登录页面登录 Keycloak 才能触发显示页面。此时,您已经位于 Keycloak 页面而不是应用程序的页面之一,因此除非您可以通过自定义主题对这些 Keycloak 页面进行非常有创意的更改,否则点击此页面实际上并不是一种选择。

第二种选择更有趣,但远非理想。每个登录的用户都可以访问帐户管理页面,该页面可以在 https://{keycloak 服务器 URL}/auth/realms/{realm name}/account 中找到。此页面允许您执行更改姓名、密码等操作,还允许您添加 OTP 设备(如果您还没有),或删除与您的帐户关联的任何现有 OTP 设备。可以通过 https://{keycloak 服务器 URL}/auth/realms/{领域名称}/account/totp 直接访问帐户管理页面的此 OTP 设备选项卡。

正如我所提到的,您没有可以访问的 API 来查看此页面上显示的二维码。访问它的唯一方法是通过对 https://{keycloak 服务器 URL}/auth/realms/{realm name}/account/totp 的 GET 请求,该请求返回我已经提到的页面的 HTML。好的,太好了,那么我们可以通过编程方式抓取 QR 码,然后将其放入应用程序的我们自己的页面中吗?呃,不,不是真的。您会看到,虽然许多 Keycloak API 端点正确地允许您在授权标头中发送不记名令牌(例如访问令牌)以访问和端点,但此页面不会接受不记名令牌作为身份验证/授权的方式。相反,它使用锁定到 Keycloak URL 的会话 cookie。当您通过 Keycloak 登录页面登录到您的应用程序时,会设置此 cookie,因此当您导航到该帐户管理页面时(已经登录),该 cookie 可供此帐户管理页面使用,并且由于帐户管理页面使用相同的服务器和域名与原始的 Keycloak 登录页面一样,它可以访问 cookie 并让您进入。您的应用程序无法将该 cookie 发送到您自己的 REST API,然后以编程方式调用帐户管理页面并抓取 QR 码,因为您的应用程序出于安全原因,无法访问它。这可能是你可以在 Keycloak 中的某个地方更改的内容,但如果有的话,我强烈建议不要更改它。

那么如果我们无法从自己的服务器上抓取页面,我们是否可以在前端做一些事情呢?好吧,如前所述,您的应用程序无权访问会话 cookie,但如果您在前端 JavaScript 中向帐户管理页面发出请求(例如使用 fetch 或 axios),则该请求将随之发送 cookie ,这样可以吗?嗯,实际上,在这种情况下,由于 CORS,您会收到一条错误消息。CORS 是跨源资源共享,为了允许访问 Keycloak 页面,您必须打开服务器上的设置以允许从您的网站地址访问它。我看过一些文章,介绍了如何在 Keycloak 上打开 CORS 设置(如果您愿意),但我对此感到非常紧张。我对 Keycloak 的内部结构及其运作方式了解不够,无法评论这有多大的安全风险,但我当然不会推荐它。这里有一些关于更改应用程序的 Keycloak 客户端的“Web Origins”设置的信息(Keycloak Angular No 'Access-Control-Allow-Origin' header is present),但这会使您的应用程序面临一些严重的潜在滥用。还有一个主要问题是,即使您抓取了二维码,设备实际上也不会添加到用户的帐户中(即使它出现在身份验证器应用程序中),直到您在二维码所在的页面中输入代码并单击“保存”。由于没有可用于完成此操作的 API 端点,因此我认为此选项也不可行。我已经尝试过您是否可以使用 https://{keycloak server URL}/auth/realms/{realm name}/protocol/openid-connect/token 处的令牌检索端点来查看是否使用您的用户名/密码/otp 代码将以某种方式“注册”您的设备并完成该过程,但是尽管您可以通过这种方式获得令牌,并且它不会抱怨 otp 代码,但它实际上并没有注意到该代码因为就其而言,用户的帐户没有注册设备。因此,我们必须使用帐户管理页面上的表格来完成此注册过程。

所以最终可能的方法是...... iframe。抱歉,是的,这是垃圾,但那就是你剩下的了。您可以将 iframe 指向您的帐户管理页面,并且由于用户已登录,因此他们将能够从您的应用程序页面查看内容。您可以使用相对定位、固定宽度和高度并删除滚动条,以确保仅显示 QR 码以及一次性代码、设备名称和“保存/取消”按钮的字段。可悲的是,这似乎是目前唯一的选择,并且由于 iframe 通常是多么令人讨厌和不可靠 - 它们肯定不会感觉对您的应用程序来说是原生的,并且您需要覆盖您的 Keycloak 主题才能获得有问题的页面看起来更像您的应用程序 - 我建议避开这一点,并使用使用 Keycloak 操作和管理 API 的标准方法。

如果您已经做到了这一步,那么恭喜您,您在 Stack Overflow 上获胜了:-)