spring security version: 6.0.1 class name: org.springframework.security.authentication.AbstractUserDetailsReactiveAuthenticationManager line: 102

I readed offical document(url: https://docs.spring.io/spring-security/reference/servlet/integrations/localization.html) about localization config, and followed the document says to config my project, but it was not working.

When I debugged my project, I found AbstractUserDetailsReactiveAuthenticationManager class(line 102) was hard coding("Invalid Credentials"). How I internationalize this message?

图片

Comment From: marcusdacoregio

Hi @hioak, I think this was an oversight and should use internationalization.

I think the code could be changed to new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Invalid Credentials"))

Are you able to submit a PR that addresses that?

Comment From: marcusdacoregio

Actually, the team told me that this might be blocked since the getMessage can be a blocking operation if it is reading from an InputStream.

That said, we cannot make the changes I proposed above but I would like to understand better what is your scenario and why you need internationalization in an authentication exception. Usually, when there is an authentication exception the exception message is not directly exposed to the users since this can be information leakage. For displaying errors to a user, generally, there is a redirect to a failure URL by the exception type and then the controller/UI displays different error messages based on the URL. eg /login?error displays invalid credentials /login?error=oidc Could not authenticate with the Identity Provider etc.

With the above context, are you able to provide more details on what you are trying to achieve?

Comment From: hioak

@ marcusdacoregio Thanks for your reply。My English is not goot, I hope you can understand what I said. I used username and password authentication, with custom user detiails service, authentication success handler and authentication failure handler. Spring security was used as a pure backend. When user login failed, authentication failure handler prevented url redirecting, and return error message body to frontend.

Here is a snippet of this code. ``` @Order(Ordered.HIGHEST_PRECEDENCE) @Bean fun webHttpSecurity(http: ServerHttpSecurity, jwtEncoder: JwtEncoder): SecurityWebFilterChain { http .csrf().disable() .cors().disable() .securityMatcher(PathPatternParserServerWebExchangeMatcher("/login")) .authorizeExchange { exchanges: AuthorizeExchangeSpec -> exchanges.anyExchange().authenticated() } .formLogin { login -> login.authenticationSuccessHandler { webFilterExchange, authentication -> val response = webFilterExchange.exchange.response val request = webFilterExchange.exchange.request val now: Instant = Instant.now() val expiry = 5L val claims = JwtClaimsSet.builder() .issuer("http://${request.localAddress}") .issuedAt(now) .expiresAt(now.plus(expiry, ChronoUnit.DAYS)) .subject(authentication.name) .build() val token = jwtEncoder.encode(JwtEncoderParameters.from(claims)).tokenValue val buffer = response.bufferFactory() .wrap(token.toByteArray(StandardCharsets.UTF_8)) response.writeWith(Mono.just(buffer)) }

        login.authenticationFailureHandler { webFilterExchange, authenticationException ->
            val request = webFilterExchange.exchange.request
            val response = webFilterExchange.exchange.response
            print(authenticationException.localizedMessage)
            val message = authenticationException.message ?: "未知错误"

            val node = ObjectMapper().createObjectNode()
            node.put("requestId", request.id)
            node.put("path", request.path.value())
            node.put("status", HttpStatus.BAD_REQUEST.value())
            node.put("error", message)
            node.put("message", if (message == "Invalid Credentials") "用户名或密码错误" else message)
            node.put("timestamp", System.currentTimeMillis())

            val buffer = response.bufferFactory()
                .wrap(node.toString().toByteArray(StandardCharsets.UTF_8))

            response.statusCode = HttpStatus.BAD_REQUEST
            response.writeWith(Mono.just(buffer))
        }
    }
return http.build()

}


**Comment From: marcusdacoregio**

Hi @hioak, I see what you are doing there. I believe that the best approach would be to always return a generic message when an `AuthenticationException` happens, you do not want to disclose any sensible information when there is an authentication problem.

What I recommend is that in your `AuthenticationFailureHandler` you always return the message as something like `Invalid username or password` or `用户名或密码错误`. Also, remember that the HTTP Status Code 401 suits better when telling the client that there is an authentication problem, instead of 400. In the code, it would be something like:

```kotlin
// ...
val node = ObjectMapper().createObjectNode()
node.put("requestId", request.id)
node.put("path", request.path.value())
node.put("status", HttpStatus.UNAUTHORIZED.value())
node.put("error", "Authentication error")
node.put("message", "用户名或密码错误")
node.put("timestamp", System.currentTimeMillis())

Comment From: hioak

@marcusdacoregio Thanks for your advice。Because of user has other status, such as locked, password overdue, or some status else. All status can get internaional message correctolly, except username-password authentication failed message, so I can not just coding like this.

Comment From: marcusdacoregio

You can catch the subclasses of AuthenticationException to handle those specific cases, take a look at the AccountStatusUserDetailsChecker.

Comment From: hioak

@marcusdacoregio Okay, I will have a try according to your advice. Thanks a lot.