使用访问权限或 ID token/token 交换建立 SSO/set cookie

Establish SSO/set cookies with access or id token/token exchange

我允许登录外部应用程序的用户通过 Keycloak 的身份代理和外部到内部令牌交换使用他们的访问令牌跳转到我们的应用程序。

现在我想在我们应用程序的嵌入式 JxBrowser 中建立 SSO 会话,类似于常规浏览器登录流程,其中在浏览器中设置了三个 cookie:AUTH_SESSION、KEYCLOAK_SESSION (_LEGACY) 和 KEYCLOAK_IDENTITY(_LEGACY).

KEYCLOAK_IDENTITY 包含一个 Serialized-ID 类型的令牌,它看起来有点类似于 ID 令牌。

是否可以使用交换的(内部)访问 and/or ID 令牌创建 KEYCLOAK_IDENTITY cookie,如果其他两个 cookie 也正确创建,这是否会建立一个有效的SSO 会话?

基本上我所缺少的是如何获取或创建 Serialized-ID 类型令牌。

实现此目的的一种方法:

  1. 在此之后实施自定义端点 example

请注意,该提供商无需在 standalone.xml 中注册就可以正常工作,我只是将 JAR 添加到 Keycloak Docker 图像。

  1. 添加一个方法来验证给定的访问令牌、查找用户、获取用户会话并在响应中设置 cookie(为简洁起见省略了大多数错误处理):

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @Path("sso")
    public Response sso(@Context final HttpRequest request) {
        final HttpHeaders headers = request.getHttpHeaders();
        final String authorization = headers.getHeaderString(HttpHeaders.AUTHORIZATION);
        final String[] value = authorization.split(" ");
        final String accessToken = value[1];
        final AccessToken token = Tokens.getAccessToken(accessToken, keycloakSession);
    
        if (token == null) {
            throw new ErrorResponseException(Errors.INVALID_TOKEN, "Invalid access token", Status.UNAUTHORIZED);
        }
    
        final RealmModel realm = keycloakSession.getContext().getRealm();
        final UriInfo uriInfo = keycloakSession.getContext().getUri();
        final ClientConnection clientConnection = keycloakSession.getContext().getConnection();
    
        final UserModel user = keycloakSession.users().getUserById(token.getSubject(), realm);
    
        final UserSessionModel userSession = keycloakSession.sessions().getUserSession(realm, token.getSessionState());
    
        AuthenticationManager.createLoginCookie(keycloakSession, realm, user, userSession, uriInfo, clientConnection);
    
        return Response.noContent().build();
    }
    

免责声明:我不完全确定此实现并不意味着任何安全问题,但由于 Tokens.getAccessToken(accessToken, keycloakSession) 对访问令牌进行了全面验证,因此只能使用有效的访问令牌设置 cookie。

对于 CORS,添加:

@OPTIONS
@Produces(MediaType.APPLICATION_JSON)
@Path("sso")
public Response preflight(@Context final HttpRequest request) {
    return Cors.add(request, Response.ok("", MediaType.APPLICATION_JSON))
            .auth()
            .preflight()
            .allowedMethods("GET", "OPTIONS")
            .build();
}

并在 sso() 中:

    return Cors.add(request, Response.ok("", MediaType.APPLICATION_JSON))
            .auth()
            .allowedMethods("GET")
            .allowedOrigins(token)
            .build();

我不确定的是为什么 Firefox 预检 GET 请求,因此有必要处理它。