Can only access Flux endpoint with @AnonymousAllowed

Hi all!

Maybe this could be a minor mistake by my side, but im really struggling to solve this.

My stack is:
Spring Boot 3.3 (Kotlin and Gradle)
Vaadin 24
Hilla with react

I already configured my application to use stateless authentication and its working ok for login and logout.

I have an endpoint that has for example this method:

@RolesAllowed("ROLE_PLAYER")
fun getCommanderSummary(): CommanderSummaryViewModel {
    ...
}

It works perfectly, it only works when logged in with ROLE_PLAYER.

Now, for the same Endpoint, i have another method that is a stream:

@RolesAllowed("ROLE_PLAYER")
fun getPlayerUpdates(): Flux<String> {
    ...
}

For this route, i am getting Access Denied, even though the other one works from the same React component.

The endpoint is called CommanderEndpoint

In the frontend, im doing like this:

const sub = CommanderEndpoint.getPlayerUpdates().onNext((update) => {
  if (update !== undefined) {
    setResourceUpdate(update);
  }
});

This is the call that is getting denied.
If i change to @AnonymousAllowed in the getPlayerUpdates method, it works.

For some reason, it seems that the subcription call made to the backend doesnt take the authentication into account.

Is this a bug or am i doing something wrong?

Thanks in advance!

Just for the sake of testing, i changed the method to:

@RolesAllowed("ROLE_PLAYER")
fun getPlayerUpdates(): String {
    return "test"
}

And in the frontend to:

const subscribeToResourceUpdates = () => {
  console.log(CommanderEndpoint.getPlayerUpdates())
};

It worked ok!
So definetly the problem lies when using the reactive endpoint.
For some reason it doesnt pass the auth information. At least when using stateless authentication.

Hi @Gustavo.42,
How does your Spring Security configuration look like? Did you add any specific configuration to support reactive endpoints? I’m not familiar with Flux, but I guess it somehow related to Spring WebFlux and based on WebSockets? In this case, you may need an additional security configuration to support stateless authentication for WebSockets as well? You could have a look at WebFlux Security :: Spring Security or at Reactive Spring Security For WebFlux REST Web Services - II | NaturalP for further advice.

My understanding is that the request is made with HTTP, it is just the server → browser that uses WebSocket. So this extra config should not be necessary.

In my demo app I have Flux endpoint using @PermitAll, i.e. it is secured, but all roles allowed, and it works ok and I do not have extras in Spring Security config.

Could you be running into this issue: Security context of push connection not updated after login · Issue #2063 · vaadin/hilla · GitHub

How did you implement login?

-Petter-

Hi all, thank you very much for the responses.
@Petter i did look into that issue when looking for answers, but i am not using the @Push on the application, so i thought maybe this wouldnt fit my use case. I am not using flow, just hilla + react

The only thing i am doing with flow is using the addFavIcon because i couldnt do it otherwise:

@SpringBootApplication
@Theme(value = "<appname>", variant = Lumo.DARK)
class <appname>Application : AppShellConfigurator {
    override fun configurePage(settings: AppShellSettings) {
        settings.addFavIcon("icon", "icons/favicon.ico", "64x64")
    }
}

Here is my security config:

@Configuration
@EnableWebSecurity
class SecurityConfig : VaadinWebSecurity() {

    @Value("\${app.secret}")
    private val appSecret: String? = null

    @Bean
    @Throws(Exception::class)
    @Lazy
    fun authenticationManager(authenticationConfiguration: AuthenticationConfiguration): AuthenticationManager {
        return authenticationConfiguration.authenticationManager
    }

    @Bean
    fun passwordEncoder(): PasswordEncoder {
        return BCryptPasswordEncoder()
    }

    @Throws(Exception::class)
    override fun configure(http: HttpSecurity) {

        http.authorizeHttpRequests { registry ->
            registry.requestMatchers(
                "/",
                "/signup",
                "/login/**",
                "/images/**",
                "/icons/**",
                "line-awesome/**",
                "/connect/UserInfoService/**",
                "/connect/CommanderEndpoint/**",
                "/h2-console/**",
                "/VAADIN/**"
            )
                .permitAll()
        }

        http.csrf { csrf ->
            csrf.ignoringRequestMatchers(
                "/",
                "/signup",
                "/login/**",
                "/images/**",
                "/icons/**",
                "line-awesome/**",
                "/connect/UserInfoService/**",
                "/connect/CommanderEndpoint/**",
                "/h2-console/**",
            )
        }

        super.configure(http)

        setLoginView(http, "/login", "/")

        http.sessionManagement { customizer -> customizer.sessionCreationPolicy(SessionCreationPolicy.STATELESS) }

        setStatelessAuthentication(
            http,
            SecretKeySpec(
                Base64.getDecoder().decode(appSecret),
                JwsAlgorithms.HS256
            ),
            "<namespace>",
            3600L
        )
    }

    @Throws(java.lang.Exception::class)
    override fun configure(web: WebSecurity) {
        web.ignoring().requestMatchers("/h2-console/**")
        super.configure(web)
    }
}

I really struggled in the beggining to make the authentication work, so some url´s in the authorizeHttpRequests and csrf were mainly tests to make things work.

Hi @Gustavo.42,
To me, it seems that your issue is the same as mentioned by @Petter: #2063

You may want to take a look at a working example of Flux returning endpoints while security is enabled in my demo app: GitHub - taefi/full-stack-signals-demo

Though, this app is not in Kotlin, and originally created as my personal playground for testing out the Full-stack Signals (coming to 24.5), I changed the first two views in the menu to subscribe to Flux returning endpoint methods secured by the @PermitAll and @RolesAllowed("ADMIN"). You can try navigating to each of these views, before login, and also after logging in with either user/user or admin/admin credentials to see the Flux returning endpoints should be working fine with the security enabled.

1 Like