Search code examples
javaspringspring-bootspring-security

I was trying to update new spring security oauth2 authorization server from 0.3.1 to 1.0.0 and got an invalide scope error.How to fix it


This is the error i get

https://spring.io/?error=invalid_scope&error_description=OAuth%202.0%20Parameter:%20scope&error_uri=https://datatracker.ietf.org/doc/html/rfc6749%23section-4.1.2.1

I get clients from database here is my oauth2 configuration:

@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain securityAuthFilterChain(HttpSecurity http) throws Exception {
    OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
    return http.formLogin().and().build();
}



@Bean
public AuthorizationServerSettings authorizationServerSettings(){return AuthorizationServerSettings.builder().build();}

@Bean
public JWKSource<SecurityContext> jwkSource(){

    RSAKey rsaKey = JwksKeys.generateRSAKey();
    JWKSet set = new JWKSet(rsaKey);
    return (j, sc) -> j.select(set);
}



@Bean
public OAuth2TokenCustomizer<JwtEncodingContext> jwtCustomizer() {
    return context -> {
        if (context.getTokenType() == OAuth2TokenType.ACCESS_TOKEN) {
            Authentication principal = context.getPrincipal();
            Set<String> authorities = principal.getAuthorities().stream()
                    .map(GrantedAuthority::getAuthority)
                    .collect(Collectors.toSet());
            for (String s:authorities)
                System.out.println(s);
            context.getClaims().claim("roles", authorities);
        }
    };
}

This is my WebSecurity Class :

 private final CorsCustomizer corsCustomizer;
private final UserService userService;
private final ClientService clientService;

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    OAuth2AuthorizationServerConfigurer configurer = new OAuth2AuthorizationServerConfigurer();
    http.apply(configurer);

    configurer.registeredClientRepository(clientService);
    corsCustomizer.customize(http);

    return http.formLogin()
            .and()
            .authorizeHttpRequests()
            .requestMatchers("/users/**").permitAll()
            .requestMatchers("/clients/**").permitAll()
            .requestMatchers("/swagger-ui/**", "/v3/api-docs/**", "/**").permitAll()
            .anyRequest().authenticated()
            .and()
            .csrf().ignoringRequestMatchers("/users/**", "/clients/**")
            .and().build();
}

@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
    var provider = new DaoAuthenticationProvider();
    provider.setUserDetailsService(userService);
    provider.setPasswordEncoder(passwordEncoder());
    return new ProviderManager(provider);
}



@Bean
public PasswordEncoder passwordEncoder(){
    return NoOpPasswordEncoder.getInstance();
}

And it is how i send request :

http://localhost:8080/oauth2/authorize?response_type=code&client_id=client&scope=openid&redirect_uri=https://spring.io/auth

This is my pom :

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>
        <version>3.0.0</version>
    </dependency>


    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
        <version>1.18.22</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-oauth2-authorization-server</artifactId>
        <version>1.0.0</version>
    </dependency>
    <dependency>
        <groupId>org.springdoc</groupId>
        <artifactId>springdoc-openapi-ui</artifactId>
        <version>1.6.4</version>
    </dependency>
    <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <version>42.2.25</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>


    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct</artifactId>
        <version>1.4.2.Final</version>
    </dependency>
</dependencies>

My client service :

private final ClientRepository clientRepository;

@Override
public void save(RegisteredClient registeredClient) {

}

@Override
public RegisteredClient findById(String id) {
    Client client = clientRepository.findById(id).orElseThrow();
    return toRegisteredClient(client);
}

@Override
public RegisteredClient findByClientId(String clientId) {
    return findById(clientId);
}

public ClientDto createClient(CreateClientRequest request) {
    var client = new Client(request);
    var scopes = request.getScopes().stream().map(ClientScope::new).collect(Collectors.toSet());
    client.setScopes(scopes);
    client.setClientRedirectUrls(request.getRedirectUris().stream()
            .map(url -> new ClientRedirectUrl(url, client))
            .collect(Collectors.toSet()));
    clientRepository.save(client);
    return new ClientDto(client);
}

public RegisteredClient toRegisteredClient(Client client) {
    RegisteredClient.Builder builder = RegisteredClient.withId(client.getId())
            .clientId(client.getId())
            .clientSecret(client.getSecret())
            .clientAuthenticationMethod(client.getAuthenticationMethod())
            .authorizationGrantTypes(
                    authorizationGrantTypes -> authorizationGrantTypes.addAll(client.getGrantTypes()))
            .redirectUris(
                    redirectUris -> redirectUris.addAll(client.getClientRedirectUrls()
                            .stream()
                            .map(ClientRedirectUrl::getUrl)
                            .collect(Collectors.toSet())))
            .scopes(scopes -> scopes.addAll(client.getScopes()
                    .stream()
                    .map(ClientScope::getScope)
                    .collect(Collectors.toSet())));
    return builder.build();
}

After i updated to oauth2 1.0.0 it began to give me invalid scope error and it doesn't redirect to login page like it did before. How can i fix it?


Solution

  • I had a similar problem - I don't know what changed under the hood from 0.3 to 1.0, but there is a sample app available for the authorization server here.

    Basically, the SecurityFilterChain bean configuration is different, and it will also require a JwtDecoder bean.