Spring-security-oauth: Use OAuth2 token_type equal "Bearer" by default everywhere

Created on 15 Apr 2015  路  23Comments  路  Source: spring-projects/spring-security-oauth

There is a line
private String tokenType = BEARER_TYPE.toLowerCase();
in DefaultOAuth2AccessToken class. Isn't it inconsistent with RFC stating that the type name should be "Bearer" (https://tools.ietf.org/html/rfc6750#section-6.1.1)?

All 23 comments

Historical reasons (that's the way we inherited this project). The RFC _used_ to say it was case insensitive, but I can't find that bit now, so it must have changed. I suspect this might be a breaking change for some apps, so we probably ought to wait till 2.1 to change it. The client always sends back the token type it gets from the server though, so in practice it probably often doesn't matter much.

This causes a problem on https://github.com/spring-cloud/spring-cloud-security/blob/master/src/main/java/org/springframework/cloud/security/oauth2/resource/UserInfoTokenServices.java#L102-L103 that creates a token without setting the type.
This means that the userInfoUri request (and tokenInfoUri) are made with Authentication: "bearer xxx" which fails for Google+.
Changing to "Bearer" fixes this issue. This issue may also be causing https://github.com/spring-cloud/spring-cloud-security/issues/59

Yeah I know, but it's not something that will get fixed here in 2.0.* (as already explained). The spring cloud bug you mentioned is unrelated, but if you are using spring cloud there are workarounds available (look through github issues for mentions of toke type). The best one is to only send tokens from a rest template that was used to obtain the token (because the token type usually comes with the response from the /token endpoint). If you can't do that then you need to customize the rest template, adding your own "authenticator" (it's a standard extension point in spring oauth).

Thanks, I am overriding loadAuthentication in my own UserInfoTokenServices so I can set the tokenType on the token in my version of getMap for now.

Hey guys, just want to point out that the token type field that is returned with an access token IS case insensitive: https://tools.ietf.org/html/rfc6749#section-4.2.2

When USING bearer tokens you need to pass the string "Bearer" (as linked in the RFC by @karwer) but it's improper to ever take the string returned as token_type and pass it along in an Authorization header without some logic involved with the token type.

IMHO not returning the token type with access tokens as lower case implies associations that are not officially there in the specs. We've dealt with many clients trying to use our OAuth server with clients that do not follow the spec, and the behavior right now on spring security (in this regard) seems perfect and on-spec to me.

OK, I missed the statement that it is case insensitive. So perhaps there is no point in changing this indeed.

Case-insensitivity applies for the token type field. However there is no ambiguity on the casing in the request headers it should be 'Bearer'. This should be addressed as multiple APIs respect this strictly.

Spring Cloud now has "Bearer" by default for the user info endpoint (which is the only place anyone ever complained about this), and a configuration setting for changing it if you need to. See https://github.com/spring-cloud/spring-cloud-security/issues/63.

We ran into this issue with someone using and old version of WSO2 API Manager (<1.8.0):

https://wso2.org/jira/browse/APIMANAGER-2921

The "bug" was fixed in that software, but we had no control over the service side and could not upgrade. Our hacky solution was to copy DefaultOAuth2AccessToken into our project and make the following changes:

private String tokenType = BEARER_TYPE; // Removed toLowerCase()

and

    public void setTokenType(String tokenType) {
        if(BEARER_TYPE.equalsIgnoreCase(tokenType)) {
            // Force "Bearer" for any case variation
            this.tokenType = BEARER_TYPE;
        } else {
            // Otherwise use as-is
            this.tokenType = tokenType;
        }
    }

Not a great solution, but it worked for us.

Just hitting the same problem, with a scala based resource server (that only checks for Bearer).
Regarding comment of @kelsin, rfc6749 describes the data returned by the auth server, https://tools.ietf.org/html/rfc6750#section-2.1 describes Bearer token handling in Request header. Here it does not say anything about case insensitive and uses "Bearer"

You'll run into this when integrating with WSO2 Identity Server 5.0

With spring-security-oauth2 2.0.8.RELEASE I just included this class.


public class WSO2OAuth2RequestAuthenticator extends DefaultOAuth2RequestAuthenticator {
  @Override
  public void authenticate(OAuth2ProtectedResourceDetails resource,
      OAuth2ClientContext clientContext, ClientHttpRequest request) {
    OAuth2AccessToken accessToken = clientContext.getAccessToken();
    String tokenType = OAuth2AccessToken.BEARER_TYPE;
    request.getHeaders().set("Authorization",
        String.format("%s %s", tokenType, accessToken.getValue()));
  }
}

@nicodewet do we need this class to be annotated with @Configuration ? or just add this to the application. I can't get it working.

@hrandika no, there's a setter for it in OAuth2RestTemplate. Create an instance and inject it yourself when you create the template, or if someone else created it for you autowire that one and call its setter.

I have used the WSO2OAuth2RequestAuthenticator class as follows, which is in line with @dsyer's advice. No need for @Configuration on WSO2OAuth2RequestAuthenticator nor LoggingRequestInterceptorOAuth2RestTemplateCustomizer.

public class LoggingRequestInterceptorOAuth2RestTemplateCustomizer
    implements UserInfoRestTemplateCustomizer {

  @Override
  public void customize(OAuth2RestTemplate template) {
    ClientHttpRequestInterceptor interceptor = new ClientHttpRequestInterceptor() {

      private final Logger log = LoggerFactory.getLogger(getClass());

      @Override
      public ClientHttpResponse intercept(HttpRequest request, byte[] body,
          ClientHttpRequestExecution execution) throws IOException {

        ClientHttpResponse response = execution.execute(request, body);

        log(request, body, response);

       return response;
      }

      private void log(HttpRequest request, byte[] body, ClientHttpResponse response)
          throws IOException {
        log.info("HttpRequest METHOD: {}", request.getMethod().toString());
        log.info("HttpRequest HEADERS: {}", request.getHeaders().toString());
        log.info("HttpRequest BODY: {}", body.toString());
        log.info("ClientHttpResponse STATUS CODE: {}", response.getStatusCode().toString());
        log.info("ClientHttpResponse HEADERS: {}", response.getHeaders().toString());
      }
    };
    List<ClientHttpRequestInterceptor> ris = new ArrayList<ClientHttpRequestInterceptor>();
    ris.add(interceptor);
    template.setInterceptors(ris);

    template.setAuthenticator(new WSO2OAuth2RequestAuthenticator());
  }

}

FYI, I have discovered this issue is preventing use of the BitBucket APIs where this section states an expectation of "Bearer". When changing "B" to "b" via manual curl testing I was able to confirm that was the exact point of failure.

Regarding the RFC 6750 references above, I don't think 4.2.2 is the right one to consider. In the scenario I encountered it is 2.1 that is relevant. It doesn't explicitly state case insensitivity or not, but "Bearer" is stated five times with an uppercase "B".

For posterity, my work around seems to be quite similar to ones above, declaring the following and passing an instance of that to the OAuth2RestTemplate constructor.

import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;

class BearerFixOAuth2ClientContext extends DefaultOAuth2ClientContext {
    @Override
    public void setAccessToken(OAuth2AccessToken accessToken) {
        if ("bearer".equals(accessToken.getTokenType())) {
            final DefaultOAuth2AccessToken fixedToken = new DefaultOAuth2AccessToken(accessToken);
            fixedToken.setTokenType("Bearer");
            super.setAccessToken(fixedToken);
        } else {
            super.setAccessToken(accessToken);
        }
    }
}

Understandably there's going to be OAuth2 servers that fall on either side of the "Bearer" | "bearer" fence, so I would like to at least see an option in DefaultOAuth2AccessToken to normalize to lowercase or not.

I just run into this issue when integrating with IBM API Connect. In this case, the authorization server returns a "bearer" token_type. When the spec explicitly states that we have to use "Authorization: Bearer XXXX" I don't see the point, in DefaultOAuth2RequestAuthenticator, of using the token_type returned by the authorization server instead of always using "Bearer".

Among the other issues listed, this creates an issue with FOS's PHP OAuth2 library (https://github.com/FriendsOfSymfony/oauth2-php/issues/98).

The fact that "token_type" is used as the prefix for the string is quite odd to me -- what is the use case that this enables? "token_type" is effectively a JSON enum (yes, I know those don't actually exist), so there's no reason to pass it through. As @codependent said, it should always simply be set to "Bearer".

We had a similar situation where the token_type must be Bearer not bearer for backward compatibility reasons. So we created TokenEnchancer and added the following block of code,

`if (OAuth2AccessToken.BEARER_TYPE.equalsIgnoreCase(accessToken.getTokenType())) {

    ((DefaultOAuth2AccessToken) accessToken).setTokenType(OAuth2AccessToken.BEARER_TYPE);

}`

According to the _OAuth 2.0 Bearer Token Usage_ spec section _2.1 Authorization Request Header field_, the format of the credentials field is:

credentials = "Bearer" 1*SP b64token

Note that in the spec, "Bearer" is upper-case in the first character.

I experienced side effects using BearerFixOAuth2ClientContext as @itzg suggested when optaining new access tokens as no OAuth2ClientContextFilter gets added by the @EnableOAuth2Client annotation when not using the injected client context. I finally extended the JdbcClientTokenServices I am using overriding saveAccessToken and getAccessToken methods. I added the following method for setting correct token type:

@Override
    public OAuth2AccessToken getAccessToken(final OAuth2ProtectedResourceDetails resource, final Authentication authentication) {
        final OAuth2AccessToken accessToken = super.getAccessToken(resource, authentication);
        setRFC675CompliantBearerType(accessToken);
        return accessToken;
    }

    @Override
    public void saveAccessToken(final OAuth2ProtectedResourceDetails resource, final Authentication authentication, final OAuth2AccessToken accessToken) {
        setRFC675CompliantBearerType(accessToken);
        super.saveAccessToken(resource, authentication, accessToken);
    }

private void setRFC675CompliantBearerType(final OAuth2AccessToken accessToken) {
        if (accessToken != null && accessToken instanceof DefaultOAuth2AccessToken) {
            ((DefaultOAuth2AccessToken)accessToken).setTokenType("Bearer");
        }
 }

May be this helps as well...

Spent hours chasing this....also causes issues with user info call on api.battle.net.

This will be resolved via #1346

Closing in favour of #1346

Was this page helpful?
0 / 5 - 0 ratings