{"action":"created","issue":{"url":"https://api.github.com/repos/spring-projects/spring-security-oauth/issues/828","repository_url":"https://api.github.com/repos/spring-projects/spring-security-oauth","labels_url":"https://api.github.com/repos/spring-projects/spring-security-oauth/issues/828/labels{/name}","comments_url":"https://api.github.com/repos/spring-projects/spring-security-oauth/issues/828/comments","events_url":"https://api.github.com/repos/spring-projects/spring-security-oauth/issues/828/events","html_url":"https://github.com/spring-projects/spring-security-oauth/issues/828","id":172719104,"number":828,"title":"Empty scopes in AC when token endpoint responds with missing scope","user":{"login":"gonzalad","id":777485,"avatar_url":"https://avatars.githubusercontent.com/u/777485?v=3","gravatar_id":"","url":"https://api.github.com/users/gonzalad","html_url":"https://github.com/gonzalad","followers_url":"https://api.github.com/users/gonzalad/followers","following_url":"https://api.github.com/users/gonzalad/following{/other_user}","gists_url":"https://api.github.com/users/gonzalad/gists{/gist_id}","starred_url":"https://api.github.com/users/gonzalad/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gonzalad/subscriptions","organizations_url":"https://api.github.com/users/gonzalad/orgs","repos_url":"https://api.github.com/users/gonzalad/repos","events_url":"https://api.github.com/users/gonzalad/events{/privacy}","received_events_url":"https://api.github.com/users/gonzalad/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":1,"created_at":"2016-08-23T14:46:26Z","updated_at":"2016-08-23T14:53:05Z","closed_at":null,"body":"As of https://tools.ietf.org/html/rfc6749#section-5.1, scope is optional in token endpoint response.\r\nIf missing, then scopes should be identical to the scopes requested by the Client.\r\n\r\nThe problem is that Spring OAuth initializes a context with no scopes in this case.\r\n\r\nMy setup : \r\n * Spring OAuth 2.0.10\r\n * Keycloak 2.1.0.Final as OIDC Server (always return empty scope in token endpoint response when user grant scopes)\r\n\r\nSample request/response to token endpoint : \r\n\r\n```\r\ngrant_type=authorization_code&code=oICYPWLKtV-NSrZ-UuC2jE_4DraLYqrlYAasSShxSSQ.c6180932-51fe-4c51-a2e1-5ab264413b1f&redirect_uri=http%3A%2F%2Flocalhost%3A9999%2Flogin\r\n```\r\n\r\n\r\n```\r\n{\r\n   \"access_token\":\"eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJHa0NNdFI2THRXb09XWlhHbmJfbk0ybHFxaGtEc20ycFZrOEc5bW83c2pZIn0.eyJqdGkiOiJhOTZlZGZhZC05ZmRjLTRjNmMtYTY4My00ZmM2ODlmNWQwZTgiLCJleHAiOjE0NzE5NjMzMzksIm5iZiI6MCwiaWF0IjoxNDcxOTYzMDM5LCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgxODAvYXV0aC9yZWFsbXMvZGVtbyIsImF1ZCI6InNhbXBsZS1hcHBsaWNhdGlvbi1jbGllbnQiLCJzdWIiOiIzNjhkODk0OC04NmRiLTQzN2EtODY2OS0xOWFiOGIwN2E4MTYiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJzYW1wbGUtYXBwbGljYXRpb24tY2xpZW50IiwiYXV0aF90aW1lIjoxNDcxOTYzMDM0LCJzZXNzaW9uX3N0YXRlIjoiMGE0MjM2NDItZjYwMy00M2JkLThhNmItOTVmODU2MGNlNjMxIiwiYWNyIjoiMSIsImNsaWVudF9zZXNzaW9uIjoiYzYxODA5MzItNTFmZS00YzUxLWEyZTEtNWFiMjY0NDEzYjFmIiwiYWxsb3dlZC1vcmlnaW5zIjpbImh0dHA6Ly9sb2NhbGhvc3Q6OTk5OSJdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJzYW1wbGUtYXBwbGljYXRpb24tY2xpZW50Ijp7InJvbGVzIjpbImhlbGxvLnNheSJdfSwiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsInZpZXctcHJvZmlsZSJdfX0sIm5hbWUiOiJ0ZXN0IHRlc3QiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJ0ZXN0IiwiZ2l2ZW5fbmFtZSI6InRlc3QiLCJmYW1pbHlfbmFtZSI6InRlc3QiLCJlbWFpbCI6ImFkcl9nb256YWxlekB5YWhvby5mciJ9.P-7nbOcjsU845423sux1UTXUFBreUIBF_l2kMnQ_nKIYVkJSOadQYeRCOJ5jEuYTz3kbN5hL6QFs5x7Kmnu3jYhVp6ix7kJzUYkiszbiRkJWzgALoS3LQz7G7cqXp0HLXuIO2pnxZ3nDa9hmk-ZhDHtYYN2tk6DJcjmviNlXs0AnD92UyVFuVu0feuzHg4z8x3u0lpMmgsEpvYT80zUHEgEoBoEdK2TpCJxRiWvPQWcjIf-GyFMDnFGpPGu24uZ9TRHOVUlxDqjwQ9WCzQ9-1npH6-fXn-0F_616PaVqBJvC2jmANQ8ibsri2VZ9nAtHZYapFOmMJ8PPWALlopkuHw\",\r\n   \"expires_in\":300,\r\n   \"refresh_expires_in\":1800,\r\n   \"refresh_token\":\"eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJHa0NNdFI2THRXb09XWlhHbmJfbk0ybHFxaGtEc20ycFZrOEc5bW83c2pZIn0.eyJqdGkiOiIxNDI1NDk5YS0zZGJmLTQ1YmEtOWNjZi1iZGU4YjY3NDNhM2MiLCJleHAiOjE0NzE5NjQ4MzksIm5iZiI6MCwiaWF0IjoxNDcxOTYzMDM5LCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgxODAvYXV0aC9yZWFsbXMvZGVtbyIsImF1ZCI6InNhbXBsZS1hcHBsaWNhdGlvbi1jbGllbnQiLCJzdWIiOiIzNjhkODk0OC04NmRiLTQzN2EtODY2OS0xOWFiOGIwN2E4MTYiLCJ0eXAiOiJSZWZyZXNoIiwiYXpwIjoic2FtcGxlLWFwcGxpY2F0aW9uLWNsaWVudCIsImF1dGhfdGltZSI6MCwic2Vzc2lvbl9zdGF0ZSI6IjBhNDIzNjQyLWY2MDMtNDNiZC04YTZiLTk1Zjg1NjBjZTYzMSIsImNsaWVudF9zZXNzaW9uIjoiYzYxODA5MzItNTFmZS00YzUxLWEyZTEtNWFiMjY0NDEzYjFmIiwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsic2FtcGxlLWFwcGxpY2F0aW9uLWNsaWVudCI6eyJyb2xlcyI6WyJoZWxsby5zYXkiXX0sImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJ2aWV3LXByb2ZpbGUiXX19fQ.HxhotcdAGOQSP2JVaPKKB-pxe48Raq741_1fFBaQgxgCjmx7dtwC2n0_YBLtdZ9eAe06buO56ovboevTXL4Ch0UYV2B5NYsSQHjZbmYlWhhS2TayVzgh8GyiS2qkNQTTk-zDNR44bBFGdSg6Bj0WfsKRLB_lf5-OVfQ2N9vqXfuBGlGxgDYTsLB8LlKOOUeEcMj71eEPf2yPIOHqpnPLyazhag7LcIW4y5BxPqqjH7Vobcogb804YMHfnI3eDbGMEAEX7yBGklUqHXanXIZir1zFH1kXbhquN0V9hldFFoGZdk28FyElNHiqbDGhqqdJRaEsdvsjw8toBlbtuCgOuA\",\r\n   \"token_type\":\"bearer\",\r\n   \"id_token\":\"eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJHa0NNdFI2THRXb09XWlhHbmJfbk0ybHFxaGtEc20ycFZrOEc5bW83c2pZIn0.eyJqdGkiOiJmOTE3NjQ3ZS0wM2MyLTRkZmMtODNlNi04MTkxNTQyOTcwYmEiLCJleHAiOjE0NzE5NjMzMzksIm5iZiI6MCwiaWF0IjoxNDcxOTYzMDM5LCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgxODAvYXV0aC9yZWFsbXMvZGVtbyIsImF1ZCI6InNhbXBsZS1hcHBsaWNhdGlvbi1jbGllbnQiLCJzdWIiOiIzNjhkODk0OC04NmRiLTQzN2EtODY2OS0xOWFiOGIwN2E4MTYiLCJ0eXAiOiJJRCIsImF6cCI6InNhbXBsZS1hcHBsaWNhdGlvbi1jbGllbnQiLCJhdXRoX3RpbWUiOjE0NzE5NjMwMzQsInNlc3Npb25fc3RhdGUiOiIwYTQyMzY0Mi1mNjAzLTQzYmQtOGE2Yi05NWY4NTYwY2U2MzEiLCJhY3IiOiIxIiwibmFtZSI6InRlc3QgdGVzdCIsInByZWZlcnJlZF91c2VybmFtZSI6InRlc3QiLCJnaXZlbl9uYW1lIjoidGVzdCIsImZhbWlseV9uYW1lIjoidGVzdCIsImVtYWlsIjoiYWRyX2dvbnphbGV6QHlhaG9vLmZyIn0.aOWNB74qPwzmT1SWmXCYR3TAMkMZlZuPqtT7RSXRoL-9S8Ts9hR0q9pymgdFCiq5SItOg7uNyg3pmZRRFcQBHO27dXehR_i1QpqILIBwwf8gDU-w04RdnI0LBDV3jbAvJpvK-fTFx2iExn6Ek3aXnARjbX3YT6i4ImSf_qQhwkpUj5IJM-8bo8QLHoXkJBTIdUJcBshCHhAYGo5W4z4MIR33Q7f1R5wfIDRE0n4oOqeRgJL3pZ5RmW2lBQf0O3AwFnZPZkMWLKXqxUFWqPnta7sKHbBi0zz_r_9YKgj3qtLxwcy6ET7e-8B8Oi6YLXiSSxL2pNddEj5ydbqxbG4Zvw\",\r\n   \"not-before-policy\":0,\r\n   \"session_state\":\"0a423642-f603-43bd-8a6b-95f8560ce631\"\r\n}\r\n```\r\n\r\n"},"comment":{"url":"https://api.github.com/repos/spring-projects/spring-security-oauth/issues/comments/241757752","html_url":"https://github.com/spring-projects/spring-security-oauth/issues/828#issuecomment-241757752","issue_url":"https://api.github.com/repos/spring-projects/spring-security-oauth/issues/828","id":241757752,"user":{"login":"gonzalad","id":777485,"avatar_url":"https://avatars.githubusercontent.com/u/777485?v=3","gravatar_id":"","url":"https://api.github.com/users/gonzalad","html_url":"https://github.com/gonzalad","followers_url":"https://api.github.com/users/gonzalad/followers","following_url":"https://api.github.com/users/gonzalad/following{/other_user}","gists_url":"https://api.github.com/users/gonzalad/gists{/gist_id}","starred_url":"https://api.github.com/users/gonzalad/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gonzalad/subscriptions","organizations_url":"https://api.github.com/users/gonzalad/orgs","repos_url":"https://api.github.com/users/gonzalad/repos","events_url":"https://api.github.com/users/gonzalad/events{/privacy}","received_events_url":"https://api.github.com/users/gonzalad/received_events","type":"User","site_admin":false},"created_at":"2016-08-23T14:53:05Z","updated_at":"2016-08-23T14:53:05Z","body":"As of now, I'm using this horrible patch in my app : \r\n\r\n```\r\n@Configuration\r\npublic class OidcClientConfiguration {\r\n\r\n    @Autowired\r\n    public void initialize(OAuth2RestTemplate template) {\r\n        template.setAccessTokenProvider(new AccessTokenProviderChain(Arrays.<AccessTokenProvider> asList(\r\n                new FixNullScopeAuthorizationCodeAccessTokenProvider(), new ImplicitAccessTokenProvider(),\r\n                new ResourceOwnerPasswordAccessTokenProvider(), new ClientCredentialsAccessTokenProvider())));\r\n    }\r\n\r\n    /**\r\n     * This class is a just fix for spring-oauth.\r\n     *\r\n     * When receiving empty scope claim as a response from token endpoint,\r\n     * Spring OAuth allocates empty scopes to OAuth Context.\r\n     * But from OAuth spec, Spring should in this case allocate the scopes requested\r\n     * by Application Client, not empty scopes.\r\n     *\r\n     * see https://tools.ietf.org/html/rfc6749#section-5.1 :\r\n     * scope OPTIONAL, if identical to the scope requested by the client\r\n     */\r\n    public static class FixNullScopeAuthorizationCodeAccessTokenProvider extends AuthorizationCodeAccessTokenProvider {\r\n\r\n        @Override\r\n        public OAuth2AccessToken obtainAccessToken(OAuth2ProtectedResourceDetails details, AccessTokenRequest request)\r\n                throws UserRedirectRequiredException, UserApprovalRequiredException, AccessDeniedException {\r\n            OAuth2AccessToken accessToken = super.obtainAccessToken(details, request);\r\n            if (accessToken.getScope() == null || accessToken.getScope().size() == 0) {\r\n                Set<String> requestedScopes =\r\n                        details.getScope().stream().flatMap(scope -> Arrays.asList(scope.split(\" \")).stream()).collect(\r\n                                Collectors.toSet());\r\n                ((DefaultOAuth2AccessToken) accessToken).setScope(requestedScopes);\r\n            }\r\n            return accessToken;\r\n        }\r\n    }\r\n}\r\n```\r\n"}}
{"actor":{"display_login":"gonzalad"}}