When trying to access any protected endpoint without including an access token in the header, the response code is 401 (which is expected), but there's no response body, unlike older OAuth2 resource server.

The older versions of OAuth2 using @EnableResourceServer used to return 401 with the following body:

{
    "error": "unauthorized",
    "error_description": "Full authentication is required to access this resource"
}

Now it just returns an empty string.

This is easily testable when adding the following test to OAuth2ResourceServerApplicationITests.java

@Test
public void performWithoutTokenThenUnauthorized()
        throws Exception {

    this.mvc.perform(get("/"))
            .andExpect(status().isUnauthorized())
            .andExpect(content().string(containsString("")));
}

Using version 5.1.1.RELEASE like so:

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-oauth2-resource-server</artifactId>
    <version>5.1.1.RELEASE</version>
</dependency>

Comment From: jgrandja

@ofir-popowski Thanks for the report. As defined in OAuth 2.0 Authorization Framework: Bearer Token Usage:

If the protected resource request does not include authentication credentials or does not contain an access token that enables access to the protected resource, the resource server MUST include the HTTP "WWW-Authenticate" response header field

The expected behaviour is to return the WWW-Authenticate response header and not a response body. Can you please confirm the WWW-Authenticate response header is returned?

Comment From: ofir-popowski

@jgrandja Both the previous and the new one return a WWW-Authenticate response header, but a bit differently: old version: WWW-Authenticate: Bearer realm="oauth2-resource", error="unauthorized", error_description="Full authentication is required to access this resource" new version: WWW-Authenticate: Bearer

edit: I'd like to point out that if token is expired, the header provides detailed explanation: WWW-Authenticate: Bearer error="invalid_token", error_description="An error occurred while attempting to decode the Jwt: Jwt expired at 2018-10-17T13:39:36Z", error_uri="https://tools.ietf.org/html/rfc6750#section-3.1"

It would be nice to have a more descriptive header in case token is missing, like expired token one

Comment From: jgrandja

@ofir-popowski

It would be nice to have a more descriptive header in case token is missing

I think this makes sense. @jzheaux What are your thoughts?

Comment From: jzheaux

@ofir-popowski Good observation. Here are two things that may help:

First, you may already know that you can customize this behavior in the DSL by wiring your own AuthenticationEntryPoint. The existing one only adds error details if they are provided in the accompanying exception, but an implementation that has some kind of default error message wouldn't be tricky.

This is the same answer for the body. If a body is helpful in your situation, then you can wire your own custom AuthenticationEntryPoint, delegating to BearerTokenAuthenticationEntryPoint for the header, with your implementation adding a body.

Second, the spec, as I read it, disallows a default error message:

If the request lacks any authentication information (e.g., the client was unaware that authentication is necessary or attempted using an unsupported authentication method), the resource server SHOULD NOT include an error code or other error information.

For example:

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="example"

However, I think that error handling is something pretty nuanced, and so the goal is to make sure that you have the right hooks to be able to deviate from the spec, should the need arise.

One piece of boilerplate that we could probably remove is the actual formatting of the error message. Right now, potential support for that is encased in a private method and it might helpful to extract that.

Comment From: sepanniemi

My observation is that currently authentication endpoint is not customizable, because web filter is initialized with a hard coded endpoint object in the jwt spec in resource server security config. Setting a custom authorization endpoint via exceptionHandling of ServerHttpSecurity does not have the desired result due to that.

https://github.com/spring-projects/spring-security/blob/master/config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java#L884

https://github.com/spring-projects/spring-security/blob/master/config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java#L988

Or am I missing something? I would also need this customization, because we would like to avoid breaking changes due to changes in error responses in our APIs when migrating to a new version of Spring Boot.

Comment From: jzheaux

@sepanniemi The file you are linking to is for Spring Security WebFlux. You are correct that the error handling in WebFlux Resource Server is not yet customizable. However, it should work fine with servlet-based Spring Security.

Though, now that you mention it, I've created a task to close that gap.

Comment From: sepanniemi

Yes, I have been doing with WebFlux myself and didn't notice this was servlet based issue. Thanks for creating the task.

Comment From: ofir-popowski

Thank you very much for your detailed answer @jzheaux :). Seeing as that was specified by the spec, I will avoid trying to customize that header myself

Comment From: jzheaux

Good deal, @ofir-popowski. Since it looks like we are on the same page now, I'll go ahead and close this.

Comment From: bcalmac

@jzheaux

Second, the spec, as I read it, disallows a default error message:

If the request lacks any authentication information (e.g., the client was unaware that authentication is necessary or attempted using an unsupported authentication method), the resource server SHOULD NOT include an error code or other error information. For example: HTTP/1.1 401 Unauthorized WWW-Authenticate: Bearer realm="example"

I don't think the spec you referenced talks about the response body, it specifies when one of the (invalid_request, invalid_token, insufficient_scope) error codes should be included in the WWW-Authenticate response header. Then it wraps up by saying that when the request lacks any authentication information then none of those 3 error codes should be included in the WWW-Authenticate header and provides the following example:

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="example" 
````

Indeed, this is different than the previous example where an error code was included:

HTTP/1.1 401 Unauthorized WWW-Authenticate: Bearer realm="example", error="invalid_token", error_description="The access token expired"


Here is the full text for context:

3.1. Error Codes

When a request fails, the resource server responds using the appropriate HTTP status code (typically, 400, 401, 403, or 405) and includes one of the following error codes in the response:

invalid_request The request is missing a required parameter, includes an unsupported parameter or parameter value, repeats the same parameter, uses more than one method for including an access token, or is otherwise malformed. The resource server SHOULD respond with the HTTP 400 (Bad Request) status code.

invalid_token The access token provided is expired, revoked, malformed, or invalid for other reasons. The resource SHOULD respond with the HTTP 401 (Unauthorized) status code. The client MAY request a new access token and retry the protected resource request.

insufficient_scope The request requires higher privileges than provided by the access token. The resource server SHOULD respond with the HTTP 403 (Forbidden) status code and MAY include the "scope" attribute with the scope necessary to access the protected resource.

If the request lacks any authentication information (e.g., the client was unaware that authentication is necessary or attempted using an unsupported authentication method), the resource server SHOULD NOT include an error code or other error information.

For example:

 HTTP/1.1 401 Unauthorized
 WWW-Authenticate: Bearer realm="example"

```

I think it makes 100% sense for the response body to match the standard spring-boot error response JSON. The OAuth2 RFC mentions nothing about the error response body, this is up to the implementation. Also, think about it in terms of consistency. How would a client be able to process errors if different conditions result in wildly different error response bodies? Sure, it's possible to have different responses for each HTTP error status, but I would submit to you that it doesn't make a for good design.