简体   繁体   English

在 Spring Cloud Gateway 中使用 Auth0,获取 ID Token 时出现问题

[英]Using Auth0 in Spring Cloud Gateway, problem with getting ID Token

We have problems with obtaining ID token using auth0 SDK. We have API Gateway based on Spring Cloud Gateway (version 3.1.4) where we try to use your auth0 platform to authenticate the users and then route the exchange to our micro services.我们在使用 auth0 SDK 获取 ID 令牌时遇到问题。我们有基于 Spring 云网关(版本 3.1.4)的 API 网关,我们尝试使用您的 auth0 平台对用户进行身份验证,然后将交换路由到我们的微服务。 To do it we would like to use ID Token and get email from it and pass this email to our micro services.为此,我们想使用 ID Token 并从中获取 email 并将此 email 传递给我们的微服务。 We log in by hitting oauth2/authorization/auth0 endpoint, we are being redirected to auth0 login page, where we provide credentials, then we get redirect back to our app.我们通过点击oauth2/authorization/auth0端点登录,我们被重定向到 auth0 登录页面,我们在其中提供凭据,然后我们被重定向回我们的应用程序。

When we configuire endpoints directly in API Gateway and mark them with @AuthenticationPrincipal OidcUser user it works and we have full user details as well as ID token.当我们直接在 API 网关中配置端点并用@AuthenticationPrincipal OidcUser user标记它们时,它可以工作,我们有完整的用户详细信息以及 ID 令牌。 When we proxy the exchange to different service we have Authorisation header in the request, which contains only header & signature part without payload in the ID token.当我们将交换代理到不同的服务时,我们在请求中有授权 header,它仅包含 header 和 ID 令牌中没有有效负载的签名部分。

We would need the payload in ID Token in order to fetch user email for mapping the user with our internal DB in our micro services.我们需要 ID Token 中的有效负载才能获取用户 email,以便将用户映射到我们微服务中的内部数据库。

What do you think would be the proper workflow in this case and how can we solve this issue?在这种情况下,您认为正确的工作流程是什么?我们如何解决这个问题?

We tried to use Rules & Actions, which I paste below, but it didn't helped us.我们尝试使用我在下面粘贴的规则和操作,但它对我们没有帮助。

Our configuration looks like this:我们的配置如下所示:

@Bean
    public SecurityWebFilterChain filterChain(ServerHttpSecurity http) throws Exception {
        return http
            .csrf().disable()
            .authorizeExchange()
            .pathMatchers("/test").authenticated()
            .anyExchange().authenticated()
            .and().oauth2Login()
            .and().logout().logoutSuccessHandler(logoutSuccessHandler())
            .and().build();
    }

In the RouteLocator in GatewayConfiguration we have filter for TokenRelay.在 GatewayConfiguration 的 RouteLocator 中,我们有 TokenRelay 过滤器。

Our Action looks like this:我们的行动看起来像这样:

exports.onExecutePostLogin = async (event, api) => {
  const namespace = 'http://test.{our_local_development_route}:8888';
  if (event.authorization) {
    api.idToken.setCustomClaim(`${namespace}/claims/email`, event.user.email);
    api.accessToken.setCustomClaim(`${namespace}/email`, event.user.email);
  }
};

And Rule:和规则:

function addEmailToAccessToken(user, context, callback) {
// This rule adds the authenticated user's email address to the access token.

  const namespace = 'http://test.{our_local_development_route}:8888';
  context.idToken[namespace + 'email'] = user.upn;
  context.accessToken[namespace + 'email'] = user.email;
  return callback(null, user, context);
}

I recently created a microservices architecture with Spring Cloud Gateway and Auth0.我最近使用 Spring Cloud Gateway 和 Auth0 创建了一个微服务架构。 I wrote about how I created it on the Auth0 blog .我在Auth0 博客上写了我是如何创建它的。 That's not the interesting part.那不是有趣的部分。 The interesting part is JHipster generates a ReactiveJwtDecoder that calls a /userinfo endpoint if some claims aren't available in the access token.有趣的部分是 JHipster 生成一个ReactiveJwtDecoder ,如果某些声明在访问令牌中不可用,它会调用/userinfo端点。 This way, the access token is enriched with identity information before it's relayed to downstream microservices.这样,在将访问令牌中继到下游微服务之前,访问令牌会使用身份信息进行丰富。

@Bean
ReactiveJwtDecoder jwtDecoder(ReactiveClientRegistrationRepository registrations) {
    Mono<ClientRegistration> clientRegistration = registrations.findByRegistrationId("oidc");

    return clientRegistration
        .map(oidc ->
            createJwtDecoder(
                oidc.getProviderDetails().getIssuerUri(),
                oidc.getProviderDetails().getJwkSetUri(),
                oidc.getProviderDetails().getUserInfoEndpoint().getUri()
            )
        )
        .block();
}

private ReactiveJwtDecoder createJwtDecoder(String issuerUri, String jwkSetUri, String userInfoUri) {
    NimbusReactiveJwtDecoder jwtDecoder = new NimbusReactiveJwtDecoder(jwkSetUri);
    OAuth2TokenValidator<Jwt> audienceValidator = new AudienceValidator(jHipsterProperties.getSecurity().getOauth2().getAudience());
    OAuth2TokenValidator<Jwt> withIssuer = JwtValidators.createDefaultWithIssuer(issuerUri);
    OAuth2TokenValidator<Jwt> withAudience = new DelegatingOAuth2TokenValidator<>(withIssuer, audienceValidator);

    jwtDecoder.setJwtValidator(withAudience);

    return new ReactiveJwtDecoder() {
        @Override
        public Mono<Jwt> decode(String token) throws JwtException {
            return jwtDecoder.decode(token).flatMap(jwt -> enrich(token, jwt));
        }

        private Mono<Jwt> enrich(String token, Jwt jwt) {
            // Only look up user information if identity claims are missing
            if (jwt.hasClaim("given_name") && jwt.hasClaim("family_name")) {
                return Mono.just(jwt);
            }
            // Retrieve user info from OAuth provider if not already loaded
            return users.get(
                jwt.getSubject(),
                s -> {
                    WebClient webClient = WebClient.create();

                    return webClient
                        .get()
                        .uri(userInfoUri)
                        .headers(headers -> headers.setBearerAuth(token))
                        .retrieve()
                        .bodyToMono(new ParameterizedTypeReference<Map<String, Object>>() {})
                        .map(userInfo ->
                            Jwt
                                .withTokenValue(jwt.getTokenValue())
                                .subject(jwt.getSubject())
                                .audience(jwt.getAudience())
                                .headers(headers -> headers.putAll(jwt.getHeaders()))
                                .claims(claims -> {
                                    String username = userInfo.get("preferred_username").toString();
                                    // special handling for Auth0
                                    if (userInfo.get("sub").toString().contains("|") && username.contains("@")) {
                                        userInfo.put("email", username);
                                    }
                                    // Allow full name in a name claim - happens with Auth0
                                    if (userInfo.get("name") != null) {
                                        String[] name = userInfo.get("name").toString().split("\\s+");
                                        if (name.length > 0) {
                                            userInfo.put("given_name", name[0]);
                                            userInfo.put("family_name", String.join(" ", Arrays.copyOfRange(name, 1, name.length)));
                                        }
                                    }
                                    claims.putAll(userInfo);
                                })
                                .claims(claims -> claims.putAll(jwt.getClaims()))
                                .build()
                        );
                }
            );
        }
    };
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 验证 Auth0 令牌 - Spring Security - Validating Auth0 Token - Spring Security Spring 云网关在尝试使用过期的 access_token 和 refresh_token 刷新令牌时出现 500 异常 - Spring Cloud Gateway Getting a 500 Exception while trying to refresh_token using expired access_token and refresh_token 如何在 Spring Cloud Gateway 上传递访问令牌 - How to Pass access token on Spring Cloud Gateway Spring Security 配置:基础认证+Spring Cloud Gateway - Spring Security Configuration: Basic Auth + Spring Cloud Gateway 在 Spring 云网关前置过滤器中获取 SecurityContextHolder - Getting SecurityContextHolder in Spring Cloud Gateway Pre Filter Spring Security:未调用自定义 UserDetailsS​​ervice(使用 Auth0 身份验证) - Spring Security: Custom UserDetailsService not being called (using Auth0 authentication) Auth0 + Grails 3 + Spring Security - Auth0 + Grails 3 + Spring Security Spring 带有自定义 Auth Server 客户端凭据的云网关与 WebClient 一起流动 - Spring Cloud Gateway with Custom Auth Server client credentials flow with WebClient Http 状态 401 spring 云网关令牌中继 - Http status 401 spring cloud gateway token relay 将“ROLE”添加到spring security / Auth0授权中 - Add 'ROLE' to spring security / Auth0 authorization
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM