spring-security: OAuth2 access token response parsing fails with nested JSON object

Summary

When parsing OAuth2 access token response a nested JSON object causes the response parsing to fail.

Actual Behavior

When attempting to use Spring Security OAuth to allow logins against a provider that responds with objects in their access token reponse an error message is shown:

An error occurred reading the OAuth 2.0 Access Token Response: JSON parse error: Cannot deserialize instance of `java.lang.String` out of START_OBJECT token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.lang.String` out of START_OBJECT token
 at [Source: (ByteArrayInputStream); line: 7, column: 14] (through reference chain: java.util.LinkedHashMap["object"]); nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize instance of `java.lang.String` out of START_OBJECT token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.lang.String` out of START_OBJECT token
 at [Source: (ByteArrayInputStream); line: 7, column: 14] (through reference chain: java.util.LinkedHashMap["object"])

Expected Behavior

According to the OAuth spec https://tools.ietf.org/html/rfc6749#section-5.1 clients must ignore values they don’t understand. The value should either end up in the additionalParameters of the OAuth2AccessTokenResponse or it should be ignored.

Configuration

Jackson is being used to parse the JSON response (seems to be default in my spring-boot application).

Version

Spring Security 5.1.3, issue also looks to be present on master.

Sample

You can see a test case that currently fails in: https://github.com/spring-projects/spring-security/compare/master...buckett:oauth-response?expand=1

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 8
  • Comments: 23 (6 by maintainers)

Commits related to this issue

Most upvoted comments

The change is in 5.3.0.BUILD-SNAPSHOT and will be in 5.3.0.RELEASE as indicated by the milestone selected with this issue.

@Gyurmatag We’ll do our best to fix this soon.

To workaround this I copied OAuth2AccessTokenResponseHttpMessageConverter into my own project and updated the tokenResponseParameters to be a Map<String, Object>. Here’s a gist of the file: https://gist.github.com/buckett/7fe338901b4fcfefb6cfce637629af3f

Then I just connect this converter up to the RestTemplate that’s used by the OAuth 2 code:

        .oauth2Login().tokenEndpoint().accessTokenResponseClient(accessTokenResposeClient());

[..snipped..]

    private OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResposeClient() {
        DefaultAuthorizationCodeTokenResponseClient client = new DefaultAuthorizationCodeTokenResponseClient();
        RestTemplate restTemplate = new RestTemplate(Arrays.asList(
                new FormHttpMessageConverter(), new OAuth2AccessTokenResponseHttpMessageConverter()));
        HttpClient requestFactory = HttpClientBuilder.create().build();
        restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(requestFactory));
        restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
        client.setRestOperations(restTemplate);
        return client;
    }

This isn’t the actual code I’m using as I’ve removed other changes that aren’t needed so it may not work as expected.

@jmgomez77 It seems to be a different issue caused by a bug in an underlying library com.nimbusds:oauth2-oidc-sdk:8.x: https://bitbucket.org/connect2id/oauth-2.0-sdk-with-openid-connect-extensions/issues/377/nimbus-8-prevent-auto-discover-mechanism

It has been fixed in 9.x and I think you can work around it with maven/gradle dependency management.

Dear all,

It seems the bug appears again in 5.4.6. Is this possible? As a result, the upgrade of Keycloak from 10.0 to 15.0 fails, since new values were added to the openid well-known configuration.

Below is the stack.

Kind regards.


Caused by: java.lang.RuntimeException: com.nimbusds.oauth2.sdk.ParseException: Unexpected type of JSON object member with key mtls_endpoint_aliases
	at org.springframework.security.oauth2.client.registration.ClientRegistrations.parse(ClientRegistrations.java:232) ~[spring-security-oauth2-client-5.4.6.jar:5.4.6]
	at org.springframework.security.oauth2.client.registration.ClientRegistrations.lambda$oidc$0(ClientRegistrations.java:157) ~[spring-security-oauth2-client-5.4.6.jar:5.4.6]
	at org.springframework.security.oauth2.client.registration.ClientRegistrations.getBuilder(ClientRegistrations.java:209) ~[spring-security-oauth2-client-5.4.6.jar:5.4.6]
	... 90 common frames omitted
Caused by: com.nimbusds.oauth2.sdk.ParseException: Unexpected type of JSON object member with key mtls_endpoint_aliases
	at com.nimbusds.oauth2.sdk.util.JSONObjectUtils.getGeneric(JSONObjectUtils.java:161) ~[oauth2-oidc-sdk-8.36.jar:8.36]
	at com.nimbusds.oauth2.sdk.util.JSONObjectUtils.getJSONObject(JSONObjectUtils.java:827) ~[oauth2-oidc-sdk-8.36.jar:8.36]
	at com.nimbusds.oauth2.sdk.as.AuthorizationServerMetadata.parse(AuthorizationServerMetadata.java:2042) ~[oauth2-oidc-sdk-8.36.jar:8.36]
	at com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata.parse(OIDCProviderMetadata.java:1362) ~[oauth2-oidc-sdk-8.36.jar:8.36]
	at org.springframework.security.oauth2.client.registration.ClientRegistrations.parse(ClientRegistrations.java:229) ~[spring-security-oauth2-client-5.4.6.jar:5.4.6]
	... 92 common frames omitted
Caused by: com.nimbusds.oauth2.sdk.ParseException: Unexpected type: class java.util.LinkedHashMap
	at com.nimbusds.oauth2.sdk.util.JSONUtils.to(JSONUtils.java:98) ~[oauth2-oidc-sdk-8.36.jar:8.36]
	at com.nimbusds.oauth2.sdk.util.JSONObjectUtils.getGeneric(JSONObjectUtils.java:159) ~[oauth2-oidc-sdk-8.36.jar:8.36]
	... 96 common frames omitted

spring: security: oauth2: client: registration: facebook: client-id: xxddd… clientSecret: secrettt… redirect-uri: https://example.com/login/oauth2/code/facebook

Issue was redirect-uri was going as http. I explicitly added callback URL with https and this fixed my issue.

I landed here after a very long excursion! I wanted to report that Strava is also returning an object in one of their response attributes. Upgrading to 5.3.0 resolved the issue for me (was on 5.2.x).

https://developers.strava.com/docs/authentication/

Example Response

{
  "token_type": "Bearer",
  "expires_at": 1568775134,
  "expires_in": 21600,
  "refresh_token": "e5n567567...",
  "access_token": "a4b945687g...",
  "athlete": {
    #{summary athlete representation}
  }
}

Thanks everyone for their hard work!