如何在外部邮件模板中为 Keycloak 用户更新配置文件生成和使用登录操作令牌

dde*_*ele 6 user-management keycloak keycloak-rest-api

我们的后端当前正在使用 KeyCloak 管理客户端 API (Java)

\n
    \n
  • 创建用户
  • \n
  • 创建角色
  • \n
  • 为用户分配角色
  • \n
  • 执行操作电子邮件(\xe2\x80\x9cUPDATE_PASSWORD\xe2\x80\x9d、\xe2\x80\x9cUPDATE_PROFILE\xe2\x80\x9d、\xe2\x80\x9cVERIFY_EMAIL\xe2\x80\x9d)
  • \n
\n

然而,我们的流程需要支持以下场景:

\n
    \n
  • 我们希望使用外部电子邮件服务/模板来发送这些邮件,而不是使用executeActionsEmailAPI 调用并让 Keycloak 向用户发送电子邮件以供他们完成个人资料
  • \n
  • 当 Keycloak 发送UPDATE_PROFILE电子邮件时,它包含/login-actions/action-token?key=eyJhbG\xe2\x80\xa6带有操作令牌的链接。
  • \n
  • 我们想将此链接嵌入到我们自己的电子邮件模板中(在 keycloak 之外)
  • \n
\n

所以问题是我是否可以使用 KeyCloak API(或其他一些机制)为 keycloak 领域内的用户生成登录操作 URL,然后我们可以在外部电子邮件模板中使用该 URL 来发送完整的注册电子邮件?

\n

小智 1

您可以通过实现特定的服务提供者接口来实现自定义服务提供者。对于您的情况,您可以实现一个自定义资源提供程序,该提供程序根据某些输入参数返回操作令牌。

文档中提供了自定义 REST API 端点的实现说明。您还可以查看这篇文章,它几乎涵盖了您的场景。

我玩了一下,这是一个例子:

公共类 ExecuteActionsTokenResourceProvider 实现 RealmResourceProvider {

    私有静态最终 Logger log = Logger.getLogger(ExecuteActionsTokenResourceProvider.class);

    私有最终 KeycloakSession 会话;

    公共 ExecuteActionsTokenResourceProvider(KeycloakSession 会话){
        this.session = 会话;
    }

    @邮政
    @Path("动作令牌")
    @Produces({MediaType.APPLICATION_JSON})
    公共响应 getActionToken(
        @QueryParam("userId") 字符串 userId,
        @QueryParam("email") 字符串电子邮件,
        @QueryParam("redirectUri") 字符串redirectUri,
        @QueryParam("clientId") 字符串 clientId,
        @Context UriInfo uriInfo) {

        KeycloakContext context = session.getContext();
        RealmModel 领域 = context.getRealm();
        intvalidityInSecs=realm.getActionTokenGenerateByUserLifespan();
        int AbsoluteExpirationInSecs = Time.currentTime() +validityInSecs;

        ClientModel 客户端 = assertValidClient(clientId, 领域);

        assertValidRedirectUri(redirectUri, 客户端);

        // 也可以参数化它
        列表 requiredActions = new LinkedList();
        requiredActions.add(RequiredAction.UPDATE_PASSWORD.name());

        字符串标记 = new ExecuteActionsActionToken(
            用户身份,
            绝对过期时间(秒),
            必需的行动,
            重定向Uri,
            客户端ID
        )。连载(
            会议,
            上下文.getRealm(),
            URI信息
        );

        返回 Response.status(200).entity(token).build();
    }

    私人无效assertValidRedirectUri(字符串redirectUri,ClientModel客户端){
        字符串重定向= RedirectUtils.verifyRedirectUri(会话,redirectUri,客户端);
        如果(重定向==空){
            抛出新的 WebApplicationException(
                ErrorResponse.error("无效的重定向 uri。", Status.BAD_REQUEST));
        }
    }

    私人 ClientModel assertValidClient(String clientId, RealmModel 领域) {
        ClientModel client =realm.getClientByClientId(clientId);
        如果(客户端==空){
            log.debugf("客户端 %s 不存在", clientId);
            抛出新的 WebApplicationException(
                ErrorResponse.error("客户端不存在", Status.BAD_REQUEST));
        }
        if (!client.isEnabled()) {
            log.debugf("客户端 %s 未启用", clientId);
            抛出新的 WebApplicationException(
                    ErrorResponse.error("客户端未启用", Status.BAD_REQUEST));
        }
        返回客户;
    }

    @覆盖
    公共对象 getResource() {
        返回这个;
    }

    @覆盖
    公共无效关闭(){
        // 没有什么要关闭的。
    }
}

然后,您可以将此类打包到 JAR 中并将其部署到 Keycloak,如部署文档中所述。Github 上也有很多这方面的例子。