web-dev-qa-db-fra.com

Spring oauth2 refresh token - Impossible de convertir le jeton d'accès en JSON

J'essaie d'utiliser un jeton d'actualisation dans une application Spring OAuth sans succès. Le système émettra un jeton d'actualisation sur une autorisation de mot de passe:

  {
  "access_token": "xxxxx",
  "token_type": "bearer",
  "refresh_token": "xxxxxx",
  "expires_in": 21599,
  "scope": "read write"
}

Mais essayer d'utiliser le jeton d'actualisation entraîne l'erreur suivante:

curl -u acme -d "grant_type = refresh_token & refresh_token = xxxxxx" http: // localhost: 9999/uaa/oauth/token

{
  "error": "invalid_token",
  "error_description": "Cannot convert access token to JSON"
}

Ma configuration de serveur d'authentification est la suivante:

@Controller
@SessionAttributes("authorizationRequest")
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
@EnableResourceServer
@ImportResource("classpath:/spring/application-context.xml")
@Configuration
public class ApplicationConfiguration extends WebMvcConfigurerAdapter {

    @RequestMapping("/user")
    @ResponseBody
    public Principal user(Principal user) {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        System.out.println(auth.toString());
        return user;
    }

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/login").setViewName("login");
        registry.addViewController("/oauth/confirm_access").setViewName("authorize");
    }

    @Configuration
    @Order(-20)
    protected static class LoginConfig extends WebSecurityConfigurerAdapter {

        @Autowired
        private AuthenticationManager authenticationManager;

        @Override
        protected void configure(HttpSecurity http) throws Exception {

            http.csrf().disable();

            // @formatter:off
            http
                .formLogin().loginPage("/login").permitAll()
            .and()
                .requestMatchers().antMatchers("/login", "/oauth/authorize", "/oauth/confirm_access")
            .and()
                .authorizeRequests().anyRequest().authenticated();
            // @formatter:on

        }

        @Bean
        @Override
        public AuthenticationManager authenticationManagerBean() throws Exception {
            return super.authenticationManagerBean();
        }
    }

    @Configuration
    public static class JwtConfiguration {

        @Bean
        public JwtAccessTokenConverter jwtAccessTokenConverter() {
            JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
            KeyPair keyPair = new KeyStoreKeyFactory(new ClassPathResource("keystore.jks"), "foobar".toCharArray())
                    .getKeyPair("test");
            converter.setKeyPair(keyPair);
            return converter;
        }

        @Bean
        public JwtTokenStore jwtTokenStore() {
            return new JwtTokenStore(jwtAccessTokenConverter());
        }
    }

    @Configuration
    @EnableAuthorizationServer
    protected static class OAuth2AuthorizationConfig extends AuthorizationServerConfigurerAdapter implements
            EnvironmentAware {

        private static final String ENV_OAUTH = "authentication.oauth.";
        private static final String PROP_CLIENTID = "clientid";
        private static final String PROP_SECRET = "secret";
        private static final String PROP_TOKEN_VALIDITY_SECONDS = "tokenValidityInSeconds";

        private RelaxedPropertyResolver propertyResolver;

        @Autowired
        private AuthenticationManager authenticationManager;

        @Autowired
        private JwtAccessTokenConverter jwtAccessTokenConverter;

        @Autowired
        private JwtTokenStore jwtTokenStore;

        @Autowired
        @Qualifier("myUserDetailsService")
        private UserDetailsService userDetailsService;

        @Autowired
        private DataSource dataSource;

        @Override
        public void setEnvironment(Environment environment) {
            this.propertyResolver = new RelaxedPropertyResolver(environment, ENV_OAUTH);
        }

        @Bean
        public TokenEnhancer tokenEnhancer() {
            return new CustomTokenEnhancer();
        }

        @Bean
        @Primary
        public DefaultTokenServices tokenServices() {
            DefaultTokenServices tokenServices = new DefaultTokenServices();
            tokenServices.setSupportRefreshToken(true);
            tokenServices.setTokenStore(jwtTokenStore);
            tokenServices.setAuthenticationManager(authenticationManager);
            tokenServices.setTokenEnhancer(tokenEnhancer());
            return tokenServices;
        }

        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
            // The order is important here - the custom enhancer must come before the jwtAccessTokenConverter.
            tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer(), jwtAccessTokenConverter));
            endpoints
                    .authenticationManager(authenticationManager)
                    .tokenEnhancer(tokenEnhancerChain)
                    .tokenStore(jwtTokenStore)
                    .userDetailsService(userDetailsService);
        }

        @Override
        public void configure(AuthorizationServerSecurityConfigurer oauthServer)
                throws Exception {
            oauthServer.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
        }

        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            clients.jdbc(dataSource);
                    /*.withClient(propertyResolver.getProperty(PROP_CLIENTID))
                    .scopes("read", "write")
                    .autoApprove(true)
                    .authorities(ClientAuthoritiesConstants.CLIENT)
                    .authorizedGrantTypes("authorization_code", "refresh_token", "password")
                    .secret(propertyResolver.getProperty(PROP_SECRET))
                    .accessTokenValiditySeconds(propertyResolver.getProperty(PROP_TOKEN_VALIDITY_SECONDS, Integer
                    .class, 1800));*/
        }
    }

    /**
     * Configures the global LDAP authentication
     */
    @Configuration
    protected static class AuthenticationConfiguration extends GlobalAuthenticationConfigurerAdapter implements EnvironmentAware {

        private static final String ENV_LDAP = "authentication.ldap.";
        private static final String PROP_SEARCH_BASE = "userSearchBase";
        private static final String PROP_SEARCH_FILTER = "userSearchFilter";
        private static final String PROP_GROUP_SEARCH_FILTER = "groupSearchFilter";
        private static final String PROP_LDAP_URL = "url";
        private static final String PROP_LDAP_USER = "userDn";
        private static final String PROP_LDAP_PASS = "password";

        private RelaxedPropertyResolver propertyResolver;

        /**
         * Maps the LDAP user to the Principle that we'll be using in the app
         */
        public UserDetailsContextMapper userDetailsContextMapper() {
            return new UserDetailsContextMapper() {
                @Override
                public UserDetails mapUserFromContext(DirContextOperations ctx, String username,
                                                      Collection<? extends GrantedAuthority> authorities) {
                    // Get the common name of the user
                    String commonName = ctx.getStringAttribute("cn");
                    // Get the users email address
                    String email = ctx.getStringAttribute("mail");
                    // Get the domino user UNID
                    String uId = ctx.getStringAttribute("uid");
                    return new CustomUserDetails(email, "", commonName, authorities);
                }

                @Override
                public void mapUserToContext(UserDetails user, DirContextAdapter ctx) {
                    throw new IllegalStateException("Only retrieving data from LDAP is currently supported");
                }

            };
        }

        @Override
        public void setEnvironment(Environment environment) {
            this.propertyResolver = new RelaxedPropertyResolver(environment, ENV_LDAP);
        }

        @Override
        public void init(AuthenticationManagerBuilder auth) throws Exception {
            auth
                    .ldapAuthentication()
                    .userSearchBase(propertyResolver.getProperty(PROP_SEARCH_BASE))
                    .groupSearchBase(propertyResolver.getProperty(PROP_SEARCH_BASE))
                    .userSearchFilter(propertyResolver.getProperty(PROP_SEARCH_FILTER))
                    .groupSearchFilter(propertyResolver.getProperty(PROP_GROUP_SEARCH_FILTER))
                    .userDetailsContextMapper(userDetailsContextMapper())
                    .contextSource()
                    .url(propertyResolver.getProperty(PROP_LDAP_URL))
                    .managerDn(propertyResolver.getProperty(PROP_LDAP_USER))
                    .managerPassword(propertyResolver.getProperty(PROP_LDAP_PASS));
        }
    }
}

Quelqu'un a-t-il une idée pourquoi le serveur d'authentification n'émet pas de nouveau jeton lorsqu'il reçoit un jeton d'actualisation valide?

6
TimS

Il semble donc que le problème soit un format refresh_token non valide. En raison de ma configuration, ce que le serveur d'authentification attendait était un JWT valide, alors que je lui envoyais un jeton porteur simple. Par conséquent, le message d'erreur "ne peut pas convertir le jeton en JSON".

Soit dit en passant, j'ai trouvé ce document utile pour comprendre comment toutes les parties de Spring OAuth s'emboîtent, ce qui m'a amené à comprendre ce qui se passait ici:

https://github.com/spring-projects/spring-security-oauth/blob/master/docs/oauth2.md

0
TimS

eu ce problème. j'envoyais le "Bearer xxxxxx ..." et le TokenEnhancer n'attendait que "xxxxx ..." sans le préfixe "Bearer"

8
Alberto

J'ai eu le même problème. Après quelques débogages, il s'est avéré que ma signature ne correspondait pas.

Dans mon cas, j'ai configuré les clés un peu différemment, et il y a un bug où la clé de signature et de vérification ne correspond pas.

https://github.com/spring-projects/spring-security-oauth/issues/1144

4
Niklas Lönn

Cela fait deux ans que je ne le fais pas si cela aide quelqu'un, mais mon même problème était dû au fait que je n'utilisais pas le tokenEnhancer que j'ai utilisé dans mon JwtTokenStore dans mon fournisseur de services de jetons DefaultTokenServices.

<!-- Access token converter -->
<bean id="jwtAccessTokenConverter"
      class="org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter">
    <property name="signingKey" value="${security.jwt.signing-key}"/>
</bean>

<!-- Token store -->
<bean id="jwtTokenStore"
      class="org.springframework.security.oauth2.provider.token.store.JwtTokenStore">
    <constructor-arg name="jwtTokenEnhancer" ref="jwtAccessTokenConverter"/>
</bean>

<!-- Creates token store services provider -->
<bean id="tokenServiceProvider"
      class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
    <property name="tokenStore"
              ref="jwtTokenStore"/>
    <!--This must be set according to z docs -->
    <property name="tokenEnhancer"
              ref="jwtAccessTokenConverter"/>
    <property name="supportRefreshToken"
              value="true"/>
    <property name="accessTokenValiditySeconds"
              value="${security.jwt.access-token-validity-seconds}"/>
    <property name="refreshTokenValiditySeconds"
              value="${security.jwt.refresh-token-validity-seconds}"/>
</bean>
1
Asefa Tasew

A également le même problème avec Spring Boot 1.5.4

Il est vraiment réel que jwtAccessTokenConverter.setVerifierKey(publicKey); ne définit pas vraiment le vérificateur (dans la valeur de débogage est nul) qui est utilisé dans -

JwtAccessTokenConverter
...protected Map<String, Object> decode(String token) {
        try {
            Jwt jwt = JwtHelper.decodeAndVerify(token, verifier);

comme solution de contournement a aidé:

private JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter jwtAccessTokenConverter = new CustomTokenEnhancer();
        jwtAccessTokenConverter.setSigningKey(jwtSigningKey);
        jwtAccessTokenConverter.setVerifier(new RsaVerifier(jwtPublicKey));
        log.info("Set JWT signing key to: {}", jwtAccessTokenConverter.getKey());

        return jwtAccessTokenConverter;
    }
0
Alex Efimov