Keycloak - 如何允许在没有注册的情况下链接帐户

Posted

技术标签:

【中文标题】Keycloak - 如何允许在没有注册的情况下链接帐户【英文标题】:Keycloak - how to allow linking accounts without registration 【发布时间】:2019-02-22 06:01:56 【问题描述】:

我正在管理一个 Keycloak 领域,其中仅添加了一个完全受信任的外部 IdP,该 IdP 旨在成为用户的默认身份验证机制。

我确实希望允许用户注册,即我想手动创建一个本地 Keycloak 用户,然后应该允许该用户将他的外部 IdP 帐户链接到预先存在的 Keycloak帐户,将电子邮件地址作为通用标识符。不应允许有权访问外部 IdP 但没有现有 Keycloak 帐户的用户进行连接。

我尝试了以下首次代理登录设置,但每当用户尝试登录时,他都会收到一条错误消息(代码:invalid_user_credentials)。

你知道我的错误可能是什么吗?

【问题讨论】:

【参考方案1】:

看起来他们在 4.5.0 版中集成了这个功能。

见automatic account link docs。

基本上您需要创建一个新流程并添加 2 个替代执行:

    如果唯一则创建用户

    自动关联代理账户

【讨论】:

在 4.5 之前的版本,比如说 3.4.3,你是如何做到这一点的? 编辑:找到答案。如果您想在 4.5.0 版本之前执行此操作,可以在此处查看:github.com/ohioit/keycloak-link-idp-with-user 我处于与原始问题类似的情况,但不幸的是,如果用户不存在,则会创建用户,但原始帖子希望没有预先存在帐户的用户无法进行身份验证. 您好@Alvaro,请问您找到解决方案了吗?仅当帐户已经存在时,我想通过链接他的 idp 帐户来登录用户。如果该帐户不在我的数据库中,则他无法登录 @hakimovic 正如我在下面发布的那样,我找到了使用自定义 javascript 身份验证器的解决方案。【参考方案2】:

根据这个讨论:

https://keycloak.discourse.group/t/link-idp-to-existing-user/1094/5

这是 keycloak 中的一个错误,他们似乎不愿意修复它 不管什么原因。我的用户很少,所以我手动解决了 查询 idp 以获取 keycloak 使用的信息,然后复制它 进入 UI 中的相关字段。所以没有注册过程 我的用户我只是自己制作。显然这是一个糟糕的解决方案 不过,我们真正需要的是有人来接管那个 PR 并且 说服维护者合并它。

这是公关:https://github.com/keycloak/keycloak/pull/6282

【讨论】:

【参考方案3】:

正如this GitHub issue response 中所述,解决方案是使用处理此问题的JavaScript authenticator。

为此,您需要执行以下操作:

    通过 https://***.com/a/63274532/550222 在您的服务器中启用 [使用 JavaScript 的自定义身份验证器[(https://www.keycloak.org/docs/latest/server_installation/#profiles) 创建文件@ 987654324@ 在您的配置目录中,其中包含以下内容:

     feature.scripts=enabled
    

    创建自定义身份验证器。您必须创建一个具有以下结构的 JAR 文件(本质上是一个 ZIP 文件):

     META-INF/keycloak-scripts.json
     auth-user-must-exist.js
    

    文件的内容在this Gist,但我也将它们包括在这里:

    META-INF/keycloak-scripts.json:

    
        "authenticators": [
            
                "name": "User must exists",
                "fileName": "auth-user-must-exists.js",
                "description": "User must exists"
            
        ]
    
    

    auth-user-must-exist.js:

    AuthenticationFlowError = Java.type("org.keycloak.authentication.AuthenticationFlowError")
    ServicesLogger = Java.type("org.keycloak.services.ServicesLogger")       
    AbstractIdpAuthenticator = Java.type("org.keycloak.authentication.authenticators.broker.AbstractIdpAuthenticator")
    IdpCreateUserIfUniqueAuthenticator = Java.type("org.keycloak.authentication.authenticators.broker.IdpCreateUserIfUniqueAuthenticator")
    
    
    var IdpUserMustExists = Java.extend(IdpCreateUserIfUniqueAuthenticator)
    
    
    function authenticate(context)                                          
        var auth = new IdpUserMustExists()                                  
            authenticateImpl: function(context, serializedCtx, brokerContext) 
                var parent = Java.super(auth)                                
    
                var session = context.getSession()                           
                var realm = context.getRealm()                               
    
                var authSession = context.getAuthenticationSession()         
    
                if (authSession.getAuthNote(AbstractIdpAuthenticator.EXISTING_USER_INFO) != null) 
                    context.attempted()                                      
                    return                                                   
                                                                            
    
                var username = parent.getUsername(context, serializedCtx, brokerContext)
                if (username == null)                                       
                    ServicesLogger.LOGGER.resetFlow(realm.isRegistrationEmailAsUsername() ? "Email" : "Username")
                    authSession.setAuthNote(AbstractIdpAuthenticator.ENFORCE_UPDATE_PROFILE, "true")
                    context.resetFlow()                                      
                    return                                                   
                                                                            
    
                var duplication = parent.checkExistingUser(context, username, serializedCtx, brokerContext)
                if (duplication == null)                                    
                    LOG.info("user not found " + username)                   
                    context.failure(AuthenticationFlowError.INVALID_USER) 
                    return                                                   
                 else                                                      
                    authSession.setAuthNote(AbstractIdpAuthenticator.EXISTING_USER_INFO, duplication.serialize())
                    context.attempted()                                      
                                                                            
                                                                            
                                                                            
    
        auth.authenticate(context)                                           
    
    

    那么,你可以定义如下:

    用户必须存在 -> 替代 自动设置现有用户 -> 替代

【讨论】:

【参考方案4】:

老实说,我对 keycloak 自动创建行为感到惊讶。我尝试添加新的身份验证流程,如此处所述https://www.keycloak.org/docs/latest/server_admin/index.html#automatically-link-existing-first-login-flow

我的流程:

1 - 如果唯一则创建用户 [ALTERNATIVE]

2 - 自动关联代理账户 [ALTERNATIVE]

我的用例:从 Github 对用户进行身份验证(Github 作为 IDP)

结果:当 github 用户使用现有的“用户名”keycloak 登录时,将 github 帐户链接到我的本地用户(基于他的用户名)。我希望使用他的电子邮件而不是用户名。

【讨论】:

【参考方案5】:

根据文档:https://www.keycloak.org/docs/latest/server_admin/index.html#detect-existing-user-first-login-flow,您必须像这样创建一个新流程:

等等 :)

【讨论】:

以上是关于Keycloak - 如何允许在没有注册的情况下链接帐户的主要内容,如果未能解决你的问题,请参考以下文章

Keycloak修改注册数据

如何在没有管理员帐户的情况下通过 REST 获取 Keycloak 用户

如何在没有登录页面的情况下将 Keycloak 与 Spring Boot(API) 集成

为啥在较新版本的 Keycloak 中不允许使用身份验证流程中的脚本

Keycloak 与 IDP 和 SSO 的集成

Keycloak 安全用户注册