LinkedIn Access Token兑换Keycloak Token时出现400 invalid_token错误求助
Keycloak集成LinkedIn时Token Exchange返回invalid_token的问题排查
问题背景
我正在为使用Keycloak做认证的应用集成LinkedIn注册/登录功能,已实现以下流程:
- 前端重定向至LinkedIn登录;
- LinkedIn携带授权码重定向至后端回调接口;
- 后端用授权码兑换LinkedIn Access Token并获取用户信息;
- 后端在Keycloak中手动创建关联LinkedIn联邦身份(federated identity)的用户;
- 调用Keycloak的Token Exchange API将LinkedIn Token转为Keycloak JWT。
相关代码
回调处理逻辑
public JwtTokenResponse processOAuthCallbackAndGetToken(String code) { // Step 1: Exchange authorization code for an access token String accessToken = exchangeAuthorizationCodeForAccessToken(code); // Step 2: Fetch user info from LinkedIn Map<String, Object> userInfo = fetchLinkedInUserProfile(accessToken); // Step 3: Create users in app if (userInfo == null) { throw new SomeException("Error occurred while fetching user info from LinkedIn."); } String email = (String) userInfo.get("email"); // create user in the application if needed // Step 4: Create user in keycloak with federatedIdentity log.info("User Info from LinkedIn ::: {}", userInfo); Map<String, Object> federatedIdentity = new HashMap<>(); federatedIdentity.put("identityProvider", "linkedin"); federatedIdentity.put("userId", userInfo.get("sub")); // LinkedIn user ID federatedIdentity.put("userName", email); userService.createKeycloakUserWithFederatedIdentity(agentByEmail.getUser(), federatedIdentity); log.info("Created user in keycloak with federated identity ::: {}", federatedIdentity); // Step 5: Exchange LinkedIn access_token with jwt return keycloakAuthServerConnector.getTokenFromLinkedInAccessToken(accessToken); }
Token Exchange调用逻辑
.... private static final String GRANT_TYPE_OAUTH_TOKEN_EXCHANGE = "urn:ietf:params:oauth:grant-type:token-exchange"; private static final String REQUESTED_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:access_token"; public static final String PROTOCOL_OPENID_CONNECT_TOKEN = "/protocol/openid-connect/token"; public com.agencyheight.dto.onboardingv2.auth.JwtTokenResponse getTokenFromLinkedInAccessToken( String accessToken) { log.info("Requesting token with linkedIn access token :: {}", accessToken); return webClient .post() .uri(PROTOCOL_OPENID_CONNECT_TOKEN) .contentType(MediaType.APPLICATION_FORM_URLENCODED) .body( BodyInserters.fromFormData("client_id", clientId) .with("client_secret", clientSecret) .with("grant_type", GRANT_TYPE_OAUTH_TOKEN_EXCHANGE) .with("subject_token", accessToken) .with("subject_issuer", "linkedin") .with("requested_token_type", REQUESTED_TOKEN_TYPE)) .retrieve() .bodyToMono(com.agencyheight.dto.onboardingv2.auth.JwtTokenResponse.class) .block(); }
错误信息
{ "error": "invalid_token", "error_description": "invalid token" }
已在Keycloak中添加LinkedIn身份提供商配置,并按官方文档开启Token Exchange权限及设置策略,但仍出现该错误,请问遗漏了什么配置或导致错误的原因是什么?
排查方案及解决办法
1. 联邦身份的用户ID字段错误
LinkedIn用户信息接口返回的用户唯一标识是id字段,而非sub。代码中使用userInfo.get("sub")作为联邦身份的userId,会导致Keycloak无法匹配LinkedIn的用户身份,进而判定token无效。
- 修复代码:将
federatedIdentity.put("userId", userInfo.get("sub"));改为federatedIdentity.put("userId", userInfo.get("id"));
2. Token Exchange请求缺少subject_token_type参数
Keycloak的Token Exchange需要明确指定subject_token的类型,LinkedIn的access token属于urn:ietf:params:oauth:token-type:access_token类型,缺少该参数会导致验证失败。
- 修复请求:在表单参数中添加
.with("subject_token_type", "urn:ietf:params:oauth:token-type:access_token")
3. Keycloak身份提供商配置验证
- 确认LinkedIn身份提供商的Alias设置为
linkedin,需与代码中subject_issuer的值完全一致; - 开启身份提供商的验证令牌选项(配置标签页),Keycloak需要验证LinkedIn access token的有效性,未开启则直接判定token无效。
4. LinkedIn Access Token权限范围不足
确保前端跳转LinkedIn时请求的scope包含openid email profile,缺少这些权限会导致LinkedIn返回的token无法被Keycloak验证通过。
5. 客户端Token Exchange权限检查
- 确认调用Token Exchange的客户端(代码中的
client_id)已启用urn:ietf:params:oauth:grant-type:token-exchange授权类型; - 在客户端的"权限"设置中,添加
token-exchange权限并启用。
内容的提问来源于stack exchange,提问作者Sabu Shakya
相关产品推荐
相关产品推荐

