Skip to content
This repository has been archived by the owner on May 31, 2022. It is now read-only.

Getting "HTTP 400 Bad Request" instead of "HTTP 401 Unauthorized" for incorrect user credentials #1906

Closed
ghost opened this issue Aug 19, 2018 · 15 comments
Assignees

Comments

@ghost
Copy link

ghost commented Aug 19, 2018

I noticed that Spring security returns HTTP 400 instead of HTTP 401 for a user who gives a wrong password. From what I was reading online it would seem that HTTP 401 is more appropriate than just HTTP 400.

@ghost ghost changed the title Getting "HTTP 400 Bad Request" instead of "HTTP 401 Unauthorized" Getting "HTTP 400 Bad Request" instead of "HTTP 401 Unauthorized" for incorrect user credentials Aug 19, 2018
@rwinch
Copy link
Contributor

rwinch commented Aug 21, 2018

I am unable to produce this with a servlet environment or a WebFlux environment. Can you provide more details on how to reproduce?

@rwinch rwinch self-assigned this Aug 21, 2018
@rwinch rwinch removed their assignment Jul 29, 2019
@kmatusiewicz
Copy link

I observed the same issue with spring-security-oauth2-2.3.4.RELEASE.

In the
org.springframework.security.oauth2.provider.password.ResourceOwnerPasswordTokenGranter#getOAuth2Authentication there is even a comment that refers to some spec:

try {
	userAuth = authenticationManager.authenticate(userAuth);
}
catch (AccountStatusException ase) {
	//covers expired, locked, disabled cases (mentioned in section 5.2, draft 31)
	throw new InvalidGrantException(ase.getMessage());
}
catch (BadCredentialsException e) {
	// If the username/password are wrong the spec says we should send 400/invalid grant
	throw new InvalidGrantException(e.getMessage());
}

However, the rfc2616 says

10.4.2 401 Unauthorized

The request requires user authentication. The response MUST include a
WWW-Authenticate header field (section 14.47) containing a challenge
applicable to the requested resource. The client MAY repeat the
request with a suitable Authorization header field (section 14.8). If
the request already included Authorization credentials, then the 401
response indicates that authorization has been refused for those
credentials.

Also, according to rfc2617

If the origin server does not wish to accept the credentials sent
with a request, it SHOULD return a 401 (Unauthorized) response.

@judos
Copy link

judos commented Nov 5, 2019

I got the same issue. Debugged for a while till I found out it's caused by Spring. Don't get it why they put 400 here.

@rwinch please remove the waiting-for-feedback tag as @kmatusiewicz already responses to you.
The question remains: Why does the class ResourceOwnerPasswordTokenGranter treat it as 400 ?

@spring-projects-issues
Copy link

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

@judos
Copy link

judos commented Dec 15, 2020

Feedback: It even says it in the code "// If the username/password are wrong the spec says we should send 400/invalid grant"
So either tell us it's fixed and which version or tell us you won't fix.

Seriously? there is the information already provided and we didn't get a response. The issuemaster just sucks. Could anyone please look at the issue? See the code part provided by kmatusiewicz...
Or just leave the ticket open if you don't find time to look at it. But auto-closing is just ridiculous. People take the time to post issues and if you don't find time to respond it will just close?

@jzheaux
Copy link
Contributor

jzheaux commented Dec 16, 2020

Sorry for the noise, @judos, let's see if I can help clear it up.

Without more information from @silentsnooc, I don't think we can address the reported issue, which is why the ticket was still waiting for feedback from the reporter. The specific behavior you and @kmatusiewicz described is one of many ways that an application could receive a 400 instead of a 401, so it didn't provide the information needed to address the reporter's issue.

That said, I can understand your frustration in not getting a response to your question. The OAuth 2.0 RFC states that the /token grant error response should return a 400:

The authorization server responds with an HTTP 400 (Bad Request)
status code (unless specified otherwise) and includes the following
parameters with the response:

I think it's also important to remember that RFC 2616 and 2617 relate to credentials specified in the Authorization header, which the Resource Owner Grant does not use for the end user's creds.

I'm going to replace the status: waiting-for-feedback to ensure that the original reporter is appropriately notified, but please note that this isn't intended to say that your question isn't valid. If you have more questions about OAuth 2.0 or Spring Security's implementation, please feel free to add a question to Stack Overflow or file a separate ticket if you believe your specific situation merits an enhancement or bug fix.

@jzheaux jzheaux self-assigned this Dec 16, 2020
@spring-projects-issues
Copy link

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

@spring-projects-issues
Copy link

Closing due to lack of requested feedback. If you would like us to look at this issue, please provide the requested information and we will re-open the issue.

@falk-stefan
Copy link

falk-stefan commented Dec 31, 2020

@jzheaux Hi!

Sorry for the late response but I am using a new account nowadays.

I am using Spring Boot 2.3.4.RELEASE in my config:

<properties>
    <spring.boot.version>2.3.4.RELEASE</spring.boot.version>
    <springdoc.version>1.2.25</springdoc.version>
</properties>

and

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-jwt</artifactId>
    <version>1.0.9.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.security.oauth</groupId>
    <artifactId>spring-security-oauth2</artifactId>
    <version>2.3.2.RELEASE</version>
</dependency>

Presenting a wrong password will produce the following log:

...
2020-12-31 09:06:25.573 DEBUG 4488 --- [io-8443-exec-10] o.s.s.w.a.i.FilterSecurityInterceptor    : Authorization successful
2020-12-31 09:06:25.573 DEBUG 4488 --- [io-8443-exec-10] o.s.s.w.a.i.FilterSecurityInterceptor    : RunAsManager did not change Authentication object
2020-12-31 09:06:25.573 DEBUG 4488 --- [io-8443-exec-10] o.s.security.web.FilterChainProxy        : /api/oauth/token reached end of additional filter chain; proceeding with original chain
2020-12-31 09:06:25.574 DEBUG 4488 --- [io-8443-exec-10] .s.o.p.e.FrameworkEndpointHandlerMapping : Mapped to org.springframework.security.oauth2.provider.endpoint.TokenEndpoint#postAccessToken(Principal, Map)
2020-12-31 09:06:25.575 DEBUG 4488 --- [io-8443-exec-10] .o.p.p.ResourceOwnerPasswordTokenGranter : Getting access token for: CLIENT_ID
2020-12-31 09:06:25.575 DEBUG 4488 --- [io-8443-exec-10] o.s.s.authentication.ProviderManager     : Authentication attempt using org.springframework.security.authentication.dao.DaoAuthenticationProvider
2020-12-31 09:06:25.745 DEBUG 4488 --- [io-8443-exec-10] o.s.s.a.dao.DaoAuthenticationProvider    : Authentication failed: password does not match stored value
2020-12-31 09:06:25.747 DEBUG 4488 --- [io-8443-exec-10] o.s.s.authentication.ProviderManager     : Authentication attempt using org.springframework.security.authentication.dao.DaoAuthenticationProvider
2020-12-31 09:06:25.905 DEBUG 4488 --- [io-8443-exec-10] o.s.s.a.dao.DaoAuthenticationProvider    : Authentication failed: password does not match stored value
2020-12-31 09:06:25.906  WARN 4488 --- [io-8443-exec-10] o.s.s.o.provider.endpoint.TokenEndpoint  : Handling error: InvalidGrantException, Bad credentials
2020-12-31 09:06:25.908  WARN 4488 --- [io-8443-exec-10] .m.m.a.ExceptionHandlerExceptionResolver : Resolved [error="invalid_grant", error_description="Bad credentials"]
2020-12-31 09:06:25.908 DEBUG 4488 --- [io-8443-exec-10] o.s.s.w.a.ExceptionTranslationFilter     : Chain processed normally
2020-12-31 09:06:25.908 DEBUG 4488 --- [io-8443-exec-10] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed

and the following JSON-response:

{
  "error" : "invalid_grant",
  "error_description" : "Bad credentials"
}

Which is fine but the HTTP code is 400 instead of 401:

image

I am not sure if that should be the case. I expected it to be a HTTP 401 Unauthorized instead. It's probably just a minor issue - if at all.

hth

@jigneshkhatri
Copy link

Same issue here. Also I am not able to handle InvalidGrantException either, to return proper message and status to user. ControllerAdvice is not handling InvalidGrantException. I just see the below logs:
o.s.s.o.provider.endpoint.TokenEndpoint : Handling error: InvalidGrantException, Bad credentials
and then 400 : [{"error":"invalid_grant","error_description":"Bad credentials"}] is returned to the user.

How to handle this exception manually and provide proper response to user?

@jzheaux
Copy link
Contributor

jzheaux commented Feb 9, 2021

Thanks, @falk-stefan, for the update. The extra detail you've added confirms that this is related to spring-security-oauth2. I'm going to transfer this issue over to that project.

@jzheaux jzheaux transferred this issue from spring-projects/spring-security Feb 9, 2021
@falk-stefan
Copy link

@jigneshkhatri Yeah, I've been there as well.

I don't have a solution for you but you might want to take a look at the AuthenticationEntryPoint. No idea if that works but in one of my older projects I have tried it with the code below. Might be a dead end though. ^^

@Component
public class AuthFailureHandler implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        System.out.println("Hello");
        // FIXME We need to return HTTP 401 (s.t. the client knows what's going on) but for some reason it's not working as intended!
        throw authException;
    }
}

@Bean
public AuthenticationEntryPoint customAuthEntryPoint(){
    return new AuthFailureHandler();
}

@jigneshkhatri
Copy link

@falk-stefan thanks for responding. I have tried that solution before, but execution does not reach there, and it throws 400 error. As per my understanding this solution only works with basic login based spring security right? Here I have implemented OAuth2 Spring Security.

@falk-stefan
Copy link

@jigneshkhatri Ah. Yeah, I think that was the problem.. the code was never really reached and I never revisited it. I never found out why this code is not getting executed to be honest.

@leo7
Copy link

leo7 commented Jul 21, 2021

This is tricky.

First of all, this is NOT to access a protected resource (like "/user/abc"), which is handled by ExceptionTranslationFilter and its configured authenticationEntryPoint (like OAuth2AuthenticationEntryPoint). For this situation you may need to check the exceptionTranslator of the authenticationEntryPoint (default to DefaultWebResponseExceptionTranslator).

Our situation here is about the oauth2 authorization server and its token endpoints (framework endpoints). So you may need to check the exceptionTranslator of AuthorizationServerEndpointsConfigurer, which can be configured by using AuthorizationServerConfigurerAdapter.

The interesting is the spec mentianed above by @jzheaux (https://datatracker.ietf.org/doc/html/rfc6749#section-5.2), it said
Selection_076,

However, when username/password does not match, spring DaoAuthenticationProvider throws BadCredentialsException, which is converted to InvalidGrantException.
Selection_077

Then the InvalidGrantException is translated to 400 by the DefaultWebResponseExceptionTranslator

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Development

No branches or pull requests

8 participants