Describe the bug When using custom implementations of GrantedAuthority with a custom AuthenticationManager, they will not be authorized, even though they contain the correct authority.

While debugging this behavior I found the following method implementation in AuthorityReactiveAuthorizationManager. I added some extra comments.

    @Override
    public Mono<AuthorizationDecision> check(Mono<Authentication> authentication, T object) {
        return authentication.filter((a) -> a.isAuthenticated())
                                // Flat mapping to Flux<? extends GrantedAuthority>
                .flatMapIterable(Authentication::getAuthorities)
                                // Checking if this.authorities contains any value from the Flux.
                .any(this.authorities::contains)
                .map((granted) -> ((AuthorizationDecision) new AuthorityAuthorizationDecision(granted, this.authorities)))
                .defaultIfEmpty(new AuthorityAuthorizationDecision(false, this.authorities));
    }

The line .any(this.authorities::contains) actually uses Objects.equals() to compare the elements from the flux to the elements in this.authorities. Since GrantedAuthority is an interface the acutal behavior of equals() depends on the implementation. AuthorityReactiveAuthorizationManager uses SimpleGrantedAuthority as implementation for this.authorities, which implement equals() as follows and in a way that excludes other implementations of GrantedAuthority altogether.

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj instanceof SimpleGrantedAuthority) {
            return this.role.equals(((SimpleGrantedAuthority) obj).role);
        }
        return false;
    }

To Reproduce 1. Create own subclass of GrantedAuthority. 1. Implement getAuthority() method, returing a String value of e.g. "SOMETHING". 1. Implement custom AuthenticationManager that authenticates a request, returning an Authentication instance containing a GrantedAuthority with value "SOMETHING" and isAuthenticated() == true. 1. Configure ServerHttpSecurity to authorize requests on some path to be only allowed with authority "SOMETHING"

.pathMatchers("/some-path/**").hasAuthority("SOMETHING")
  1. Try making a valid authenticated request.

Expected behavior Requested should be authorized.

AuthorityReactiveAuthorizationManager should not compare instances of GrantedAuthority using Objects.equals by utilizing .any(this.authorities::contains), but instead compare the String values returned from getAuthority().

When using instances of SimpleGrantedAuthority for own authentications, the authorization works because of the specific implementation of equals() in that class.

Sample None yet. If the description is not sufficient enough, I can provide a sample.

Comment From: terminux

Fixed via 86ed937

Related issues:

  • 10566

Comment From: ghost

Thanks! Sorry about the duplicate. I guess I didn't remove the "issue:open" filter, when searching for existing issues regarding this problem.