View based authorization with OAuth2 on EntraID

I have configured OAuth Authentication with EntraID, but I’m having problems getting the view-based authentication to work, with the @RolesAllowed annotation etc.
I’m on Vaadin 24.9.6 and VaadinSecurityConfigurer is not available as described in the documentation. Am I missing something?
Also injecting the AuthenticationContext produces a “no such bean” exception.

Here is my SecurityConfiguration - this one works and handles all the frontend/backend communication etc. How can I get the view based annotations to work and how can I access the roles through code to show or hide components based on the user role?

@Configuration
@EnableWebSecurity
@Import(VaadinAwareSecurityContextHolderStrategyConfiguration.class)
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http, RouteUtil routeUtil, RequestUtil requestUtil)
            throws Exception {
        http
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers(routeUtil::isRouteAllowed).permitAll()
                        .requestMatchers(requestUtil::isFrameworkInternalRequest).permitAll()
                        .anyRequest().authenticated()
                )
                .oauth2Login(oauth -> oauth
                        .userInfoEndpoint(userInfo -> userInfo
                                .oidcUserService(oidcUserService()))
                        .defaultSuccessUrl("/"))
                .logout(logout -> logout
                        .logoutSuccessUrl("/")
                )
                .csrf(csrf -> csrf.ignoringRequestMatchers(requestUtil::isFrameworkInternalRequest))
                .headers(headers -> headers.frameOptions(frame -> frame.sameOrigin()));

        return http.build();
    }


    @Bean
    public OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService() {
        return userRequest -> {
            OidcUserService delegate = new OidcUserService();
            OidcUser oidcUser = delegate.loadUser(userRequest);

            List<GrantedAuthority> mappedAuthorities = oidcUser.getAuthorities().stream()
                    .collect(Collectors.toList());

            List<String> groupIds = oidcUser.getClaimAsStringList("groups");

            if (groupIds != null) {
                for (String groupId : groupIds) {
                    switch (groupId) {
                        case "11111111-1111-1111-1111-111111111111":
                            mappedAuthorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
                            break;
                        case "22222222-2222-2222-2222-222222222222":
                            mappedAuthorities.add(new SimpleGrantedAuthority("ROLE_USER"));
                            break;
                    }
                }
            }

            return new DefaultOidcUser(mappedAuthorities, oidcUser.getIdToken(), oidcUser.getUserInfo());
        };
    }

}

VaadinSecurityConfigurer is available in Vaadin 24.9.

It looks like your configuration is missing the Vaadin security configuration

http.with(VaadinSecurityConfigurer.vaadin(), configurer -> {
            .....
        })

This is weird. It seems you are missing the Vaadin auto confiuguration. The bean is published by SpringSecurityAutoConfiguration

Can you double-check your dependencies to ensure vaadin-spring (and SB starter) is at version 24.9.x ?

Probably the role prefix is missing

Check out my blog Securing Vaadin Applications with Microsoft Entra - Martinelli

1 Like

Thanks! That put me on the right track. I was missing the vaadin-spring package completely. I took my POM from another huge Vaadin project instead of creating a new one with the starter. That older project did not have it and did not need it up to now. So I guess I’ll now add it to both ;-)

So for everyone looking for a running configuration to use with EntraID specifically, this is the working code:

@Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http, RouteUtil routeUtil, RequestUtil requestUtil)
            throws Exception {
        http
                .oauth2Login(oauth -> oauth
                        .userInfoEndpoint(userInfo -> userInfo
                                .oidcUserService(oidcUserService()))
                        .defaultSuccessUrl("/"))
                .with(VaadinSecurityConfigurer.vaadin(), configurer -> {
                    configurer.oauth2LoginPage("/oauth2/authorization/entraid"); # The last part is specific  to what you named registration ID 
                })
                .logout(logout -> logout
                        .logoutSuccessUrl("/")
                );

        return http.build();
    }

@Bean
    public OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService() {
        return userRequest -> {
            OidcUserService delegate = new OidcUserService();
            OidcUser oidcUser = delegate.loadUser(userRequest);

            List<GrantedAuthority> mappedAuthorities = oidcUser.getAuthorities().stream()
                    .collect(Collectors.toList());

            List<String> groupIds = oidcUser.getClaimAsStringList("groups");

            if (groupIds != null) {
                for (String groupId : groupIds) {
                    switch (groupId) {
                        case "11111111-1111-1111-1111-111111111111":
                            mappedAuthorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
                            break;
                        case "22222222-2222-2222-2222-222222222222":
                            mappedAuthorities.add(new SimpleGrantedAuthority("ROLE_USER"));
                            break;
                    }
                }
            }

            return new DefaultOidcUser(mappedAuthorities, oidcUser.getIdToken(), oidcUser.getUserInfo());
        };
    }

Have you noticed that there is an official Spring Boot Integration from Microsoft?

Yes, I actually started off with that, but learned the hard way (again) that it is best to stay away from everything Microsoft.

What was the problem?
We use the Entra starter in production for more than a year

It’s overly convoluted, the documentation is a mess, the example repository has the relevant examples all named the same on the Maven side, so that it took me a lot of time to find the right example - and then the code did not work. So after some time I decided to go with OAuth2 because it is an open standard and I can reuse the code for another project that does not use EntraID.

It’s a pitty you didn’t find my example.