Vaadin 24 + Spring Security + AzureAD/EntraID Login + JWTs

Hi,

We’ve been running an internal app without any issues for long time using the username and password login. This method is used for B2C access but now we want to add access for internal users using Entra ID / Azure AD.

So, we need to maintain both methods. Moreover, we’re using the JWTs tokens for the Vaadin Auth with the split cookie method.

We’re facing 2 issues:
1- We have no way at the moment to see on the auth token where the authentication comes from, if a user logged in with username and password or via OAuth.

2- The AAD plugin loads the roles from what’s configured on AAD and we’d need to delegate this on the application after checking the username on the database.

If we manage to solve point 2, it doesn’t really matter to know how the user logged in but it would still be useful to add a claim on the JWT token that indicates the source.

@Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests(a -> a.requestMatchers(new AntPathRequestMatcher("/images/*.png")).permitAll());
        http.authorizeHttpRequests(a -> a.requestMatchers(new AntPathRequestMatcher("/api/**")).permitAll());
        http.authorizeHttpRequests(a -> a.requestMatchers(new AntPathRequestMatcher("/login/**")).permitAll());
        http.csrf(csrf -> csrf.ignoringRequestMatchers(new AntPathRequestMatcher("/api/**")));
        http.headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable));



        super.configure(http);
        http.oauth2Login((oauth) -> {
            oauth.loginPage("/login");
            oauth.userInfoEndpoint(info -> {
                //info.oidcUserService(oidcUserService);
            });
        });

        setLoginView(http, LoginView.class, LOGOUT_URL);
        //setOAuth2LoginPage(http, "/login/oauth2/code");
        setStatelessAuthentication(http,
                new SecretKeySpec(Base64.getDecoder().decode(authSecret), JwsAlgorithms.HS256),
                ISSUER,
                TOKEN_TIMEOUT);
    }

And we’re using the following dependency:

<dependency>
            <groupId>com.azure.spring</groupId>
            <artifactId>spring-cloud-azure-starter-active-directory</artifactId>
            <version>5.15.0</version>
        </dependency>

any ideas will be welcome!

Found the answer myself.

on SecurityConfiguration, I added a customOidcUserService

http.oauth2Login((oauth) -> {
            oauth.loginPage("/login");
            oauth.userInfoEndpoint(info -> {
                info.oidcUserService(customOidcUserService());
            });
        });
private OAuth2UserService<OidcUserRequest, OidcUser> customOidcUserService() {
        return userRequest -> {
            OidcUser oidcUser = this.oidcUserService.loadUser(userRequest);
            Set<GrantedAuthority> mappedAuthorities = new HashSet<>();
            //Add authorities based on DB authentication or any other method.
            mappedAuthorities.add(new SimpleGrantedAuthority("ROLE_TESTING"));
            mappedAuthorities.add(new SimpleGrantedAuthority("ROLE_MASTER"));

            NamedOidcUser newOidcUser = new NamedOidcUser(mappedAuthorities, oidcUser.getIdToken(), oidcUser.getUserInfo(),oidcUser.getName());
            log.info("OIDCUser is = {}", newOidcUser);
            return newOidcUser;
        };
    }

More info at the end of the article