After Spring Boot 2 upgade authorization server returns “At least one redirect_uri must be registered with the client.”

核能气质少年 提交于 2021-02-17 05:30:09

问题


I upgraded our authorization server from Spring Boot 1.5.13.RELEASE to 2.1.3.RELEASE, and now I can authenticate, but I can no longer access the site. Here is the resulting URL and error after the POST to /login.

https://auth-service-test-examle.cfapps.io/oauth/authorize?client_id=proxy-service&redirect_uri=http://test.example.com/login&response_type=code&state=QihbF4

   OAuth Error

   error="invalid_request", error_description="At least one redirect_uri must be registered with the client."

To troubleshoot, I started a fresh project based on the Spring Security 5.1.4.RELEASE sample "oauth2authorizationserver." I layered on the features used in our Spring Boot 1.5.13 authorization server making sure the unit tests passed (except one test class). If I @Ignore the failing tests and deploy the code I get the problem described above.

The problem is reproducible in the AuthenticationTests.loginSucceeds() JUnit test that passed before the upgrade. It expects a 302, but now it gets a 403 because it goes to the root of the authentication server. I published the entire example on GitHub spring-security-5-upgrade_sso-auth-server

Clone the project and run the unit tests and you will see the failures.

Here are some of the key settings that can be found in the project on GitHub.

  public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {

  private final String privateKey;

  private final String publicKey;

  private final AuthClientDetailsService authClientDetailsService;

  private final AuthenticationManager authenticationManager;

  private final AuthUserDetailsService authUserDetailsService;

  @Autowired
  public AuthServerConfig(
      @Value("${keyPair.privateKey}") final String privateKey,
      @Value("${keyPair.publicKey}") final String publicKey,
      final AuthClientDetailsService authClientDetailsService,
      final AuthUserDetailsService authUserDetailsService,
      final AuthenticationConfiguration authenticationConfiguration) throws Exception {
    this.privateKey = privateKey;
    this.publicKey = publicKey;
    this.authClientDetailsService = authClientDetailsService;
    this.authUserDetailsService = authUserDetailsService;
    this.authenticationManager = authenticationConfiguration.getAuthenticationManager();
  }

  @Override
  public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
    clients.withClientDetails(authClientDetailsService);
  }

  @Override
  public void configure(final AuthorizationServerEndpointsConfigurer endpoints) {
    endpoints
        .authenticationManager(authenticationManager)
        .accessTokenConverter(accessTokenConverter())
        .userDetailsService(authUserDetailsService)
        .tokenStore(tokenStore());

  }

    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }


  @Bean
  public JwtAccessTokenConverter accessTokenConverter() {
    final JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
    converter.setSigningKey(privateKey);
    converter.setVerifierKey(publicKey);
    return converter;
  }

}

public class GlobalAuthenticationConfig extends GlobalAuthenticationConfigurerAdapter {

  private final AuthUserDetailsService authUserDetailsService;

  @Autowired
  public GlobalAuthenticationConfig(final AuthUserDetailsService authUserDetailsService) {
    this.authUserDetailsService = authUserDetailsService;
  }

  @Override
  public void init(AuthenticationManagerBuilder auth) throws Exception {
    auth
        .userDetailsService(authUserDetailsService)
        .passwordEncoder(new BCryptPasswordEncoder());
  }
}

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

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

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

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
      auth.parentAuthenticationManager(authenticationManager);
    }
  }

public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
  private final AuthUserDetailsService authUserDetailsService;

  @Autowired
  public WebSecurityConfig(AuthUserDetailsService authUserDetailsService) {
    this.authUserDetailsService = authUserDetailsService;
  }

  @Override
  public void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth
        .userDetailsService(authUserDetailsService)
        .passwordEncoder(new BCryptPasswordEncoder());
  }

}

What else needs to be done in Spring Boot 2.1.3.RELEASE to redirect the user back to the original webpage?


回答1:


It's important that OAuth 2.0 clients register a redirect_uri with Authorization Servers as an Open Redirector mitigation. As such, Spring Boot 2.1.x has this as its default behavior, which is why you're seeing the error.

You can do one of two things:

Add redirect_uris, one for each client

Ideally, you'd update your clients to each have a registered redirect_uri, which would likely be retrieved in an implementation of ClientDetailsService:

public class MyClientDetailsService implements ClientDetailsService {
    private final MyRespository myRepository;

    public ClientDetails loadClientByClientId(String clientId) {
        return new MyClientDetails(this.myRepository.getMyDomainObject(clientId));
    }

    private static class MyClientDetails extends MyDomainObject implements ClientDetails {
        private final MyDomainObject mine;

        public MyClientDetails(MyDomainObject delegate) {
            this.delegate = delegate;
        }

        // implement ClientDetails methods, delegating to your domain object

        public Set<String> getRegisteredRedirectUri() {
            return this.delegate.getRedirectUris();
        }
    }
}

This setup with the private subclass - while not necessary - is nice because it doesn't tie the domain object directly to Spring Security.

Add a custom RedirectResolver

Or, you can customize the RedirectResolver, though this wouldn't secure against Open Redirects, which was the original reason for the change.

public MyRedirectResolver implements RedirectResolver {
    private final RedirectResolver delegate = new DefaultRedirectResolver();

    public String resolveRedirect(String redirectUri, ClientDetails clientDetails) {
        try {
            return this.delegate.resolveRedirect(redirectUri, clientDetails);
        } catch ( InvalidRequestException ire ) {
            // do custom resolution
        }
    }
}


来源:https://stackoverflow.com/questions/55382404/after-spring-boot-2-upgade-authorization-server-returns-at-least-one-redirect-u

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!