GraphQL with Spring Boot Security returning 403

GraphQL with Spring Boot Security returning 403


0

I have been trying to implement GraphAPI methods authorization. But, always get 403.

Environment:

  • JDK 17
  • Spring Boot 3.1.5
  • MongoDB
  • Spring Security
  • Graph QL
  • OS: MacOS

JDK 17

My application file looks like:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@SpringBootApplication(exclude = {SecurityAutoConfiguration.class,
    UserDetailsServiceAutoConfiguration.class})
@EnableAspectJAutoProxy
public class JoblobApplication {

    public static void main(String[] args) {

        SpringApplication.run(JoblobApplication.class, args);
    }

}

Book entity looks like:

@Document(collection = "books")
@Getter
@Setter
public class Book {
    @Id
    private String id;
    private String title;
    private String author;

    // Getters and setters
}

Book Repository :

public interface BookRepository extends MongoRepository<Book, String> {
}

GraphQL Resolver:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface GraphQLResolver {
}


@Component
public class ResourceResolver {



    @Autowired
    private BookRepository bookRepository;

    @QueryMapping
    @PreAuthorize("hasRole('ROLE_USER')")
    public List<Book> books() {
        return bookRepository.findAll();
    }

    @QueryMapping
    @PreAuthorize("hasRole('ROLE_USER')")
    public Book bookById(String id) {
        return bookRepository.findById(id).orElse(null);
    }
}

GraphQL Aspect:

@Aspect
@Component
@Order(1)
public class SecurityQraphQLAspect {
    @Before("allGraphQLResolverMethods() && isDefinedInApplication() && !isMethodAnnotatedAsUnsecured()")
    public void doSecurityCheck() {
        if (SecurityContextHolder.getContext() == null ||
            SecurityContextHolder.getContext().getAuthentication() == null ||
            !SecurityContextHolder.getContext().getAuthentication().isAuthenticated() ||
            AnonymousAuthenticationToken.class.isAssignableFrom(SecurityContextHolder.getContext().getAuthentication().getClass())) {
            throw new AccessDeniedException("User not authenticated");
        }
    }
    @Pointcut("@annotation(resolver.GraphQLResolver)")
    private void allGraphQLResolverMethods() {
    }
     @Pointcut("within(resolver.*)")
    private void isDefinedInApplication() {
    }
    @Pointcut("execution(public java.lang.String resolver.ResourceResolver.unsecuredResource())")
    private void isUnsecuredResourceMethod() {
    }

    @Pointcut("@annotation(oresolver.Unsecured)")
    private void isMethodAnnotatedAsUnsecured() {
    }
}

Annotation Unsecured:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Unsecured {
}

Whenever I try to access using graphiql it returns 403. If I add @Unsecured to any query method it returns the response.

Security config looks like:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration {
    private static final String REALM_ACCESS_CLAIM = "realm_access";
    private static final String ROLES_CLAIM = "roles";

    @Bean
    public SecurityFilterChain configure(HttpSecurity http) throws Exception {
        http
            .oauth2Client()
            .and()
            .oauth2Login()
            .tokenEndpoint()
            .and()
            .userInfoEndpoint();

        http
            .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.ALWAYS);

        http
            .authorizeHttpRequests()
            .requestMatchers("/unauthenticated", "/graphiql","/graphql", "/oauth2/**", "/login/**").permitAll()
            .anyRequest()
            .fullyAuthenticated()
            .and()
            .logout()
            .logoutSuccessUrl(
                "https://localhost:8180/realms/test/protocol/openid-connect/logout?redirect_uri=https://localhost:9999/");
        http.csrf().disable();
        return http.build();
    }

    @Bean
    @SuppressWarnings("unchecked")
    public GrantedAuthoritiesMapper userAuthoritiesMapperForKeycloak() {
        return authorities -> {
            Set<GrantedAuthority> mappedAuthorities = new HashSet<>();
            var authority = authorities.iterator().next();
            boolean isOidc = authority instanceof OidcUserAuthority;

            if (isOidc) {
                var oidcUserAuthority = (OidcUserAuthority) authority;
                var userInfo = oidcUserAuthority.getUserInfo();

                if (userInfo.hasClaim(REALM_ACCESS_CLAIM)) {
                    var realmAccess = userInfo.getClaimAsMap(REALM_ACCESS_CLAIM);
                    var roles = (Collection<String>) realmAccess.get(ROLES_CLAIM);
                    mappedAuthorities.addAll(generateAuthoritiesFromClaim(roles));
                }
            } else {
                var oauth2UserAuthority = (OAuth2UserAuthority) authority;
                Map<String, Object> userAttributes = oauth2UserAuthority.getAttributes();

                if (userAttributes.containsKey(REALM_ACCESS_CLAIM)) {
                    var realmAccess = (Map<String, Object>) userAttributes.get(REALM_ACCESS_CLAIM);
                    var roles = (Collection<String>) realmAccess.get(ROLES_CLAIM);
                    mappedAuthorities.addAll(generateAuthoritiesFromClaim(roles));
                }
            }
            return mappedAuthorities;
        };
    }

    Collection<GrantedAuthority> generateAuthoritiesFromClaim(Collection<String> roles) {
        return roles.stream().map(role -> new SimpleGrantedAuthority("ROLE_" + role.toUpperCase(Locale.ROOT))).collect(Collectors.toList());
    }

1

  • Please enable spring security DEBUG logs and edit your question to include the full logs.

    – Toerktumlare

    1 hour ago


Load 6 more related questions


Show fewer related questions

0



Leave a Reply

Your email address will not be published. Required fields are marked *