Docs

Test View Access Control

Verify anonymous, authorized, and unauthorized navigation in Spring and Quarkus browserless tests.

This page covers Spring Security and Quarkus Security in separate sections. For Java EE/CDI, use the CDI setup and your application’s CDI access-control beans and login flow; the security annotations below do not configure Weld.

First configure view protection in your application. These examples assume a public default route, a login view, and views restricted to specific roles. Adapt the route names and roles to your application.

Spring Security

Set Up View Access Control

To apply view access control, Vaadin requires a NavigationAccessControl to be registered as a BeforeEnterListener for the UI. For @SpringBootTest annotated tests, the checker is created and configured automatically. However, when testing with a restricted ApplicationContext, you may want to perform the setup yourself in a Configuration class by providing a VaadinServiceInitListener that executes this step.

Source code
Set Up NavigationAccessControl for Plain Spring Project
@Configuration
class TestViewSecurityConfig {

    @Bean
    VaadinServiceInitListener setupViewSecurityScenario() {
        SpringNavigationAccessControl accessControl = new SpringNavigationAccessControl();
        accessControl.setLoginView(LoginView.class);
        return event -> {
            event.getSource().addUIInitListener(uiEvent -> {
                uiEvent.getUI().addBeforeEnterListener(accessControl);
            });
        };
    }
}

If you’re using the Vaadin Spring Add-On, you can instead import the out-of-the-box NavigationAccessControlInitializer. It requires only that you define a NavigationAccessControl bean.

Source code
Set Up NavigationAccessControl with Vaadin Spring Add-On
@Configuration
@Import({NavigationAccessControlInitializer.class})
class TestViewSecurityConfig {

    @Bean
    NavigationAccessControl navigationAccessControl() {
        return new SpringNavigationAccessControl();
    }
}

Testing with Spring Security Annotations

With this support, you can use Spring Security test annotations — such as @WithMockUser, @WithAnonymousUser, or @WithUserDetails — to simulate different authentication scenarios with test method granularity. More information is available on the Spring Security documentation site. Authentication details are available before creating the UI instance and navigating to the default route. This way redirects to the login view aren’t performed when simulating logged-in users. In the same way, custom redirect logic for authenticated users works as expected.

To use Spring Security test annotations, first make sure the dependency is added to the project.

Source code
XML
<dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-test</artifactId>
        <scope>test</scope>
</dependency>
Caution
Overriding beans with @MockitoBean or @MockBean makes Spring cache a separate application context for that test class. In a multi-class run, this can prevent the simulated user from being applied during navigation, causing protected views to redirect unexpectedly to the login view — sometimes in other test classes. If security navigation tests fail only when the whole suite runs, suspect a bean override elsewhere. See Using a Reduced Application Context for a context-friendly way to replace services.

Then extend SpringBrowserlessTest and annotate test methods to set up an authentication scenario. For the simplest use cases, use @WithMockUser or @WithAnonymousUser, providing the username and roles that should be granted.

Source code
Tests with Mock Users
@SpringBootTest
public class ViewSecurityTest extends SpringBrowserlessTest {

    @Test
    @WithAnonymousUser
    void anonymousUser_protectedView_redirectToLogin() {
        navigate("protected", LoginView.class);
    }

    @Test
    @WithAnonymousUser
    void anonymousUser_publicView_signInLinkPresent() {
        // public view is default page
        Assertions.assertInstanceOf(PublicView.class, getCurrentView());

        Anchor anchor = find(Anchor.class).withText("Sign in").single();
        Assertions.assertTrue(
                test(anchor).isUsable(),
                "Sign in link should be available for anonymous user");
    }

    @Test
    @WithMockUser(username = "admin", roles = "ADMIN")
    void adminUser_adminView_viewShown() {
        navigate(AdminRoleView.class);

        Assertions.assertTrue(
                find(Avatar.class).single().isVisible(),
                "Avatar should be visible for logged users");
    }
}

When custom User objects or complex grant rules should be used, provide a custom UserDetailsService and annotate the test method with @WithUserDetails.

Source code
Tests with Mock UserDetailsService
@ContextConfiguration(classes = SecurityTestConfig.class)
class SpringUnitSecurityTest extends SpringBrowserlessTest {

    @Test
    @WithUserDetails("admin")
    void superuser_adminView_viewShown() {
        navigate(AdminRoleView.class);

        Assertions.assertTrue(
                find(Avatar.class).single().isVisible(),
                "Avatar should be visible for logged users");
    }

    @Test
    @WithUserDetails
    void user_adminView_accessDenied() {
        RouteNotFoundError errorView = navigate("admin-role",
                RouteNotFoundError.class);
        Assertions.assertTrue(
                errorView.getElement().getChild(0).getOuterHTML()
                        .contains("Reason: Access denied"),
                "Admin view should be accessible only by users with ADMIN role");
    }


}

@Configuration
class SecurityTestConfig {

    @Bean
    UserDetailsService mockUserDetailsService() {

        return new UserDetailsService() {
            @Override
            public UserDetails loadUserByUsername(String username)
                    throws UsernameNotFoundException {
                if ("user".equals(username)) {
                    return new User(username, UUID.randomUUID().toString(),
                            List.of(
                                new SimpleGrantedAuthority("ROLE_DEV"),
                                new SimpleGrantedAuthority("ROLE_USER")
                        ));
                }
                if ("admin".equals(username)) {
                    return new User(username, UUID.randomUUID().toString(),
                            List.of(
                                new SimpleGrantedAuthority("ROLE_SUPERUSER"),
                                new SimpleGrantedAuthority("ROLE_ADMIN")
                        ));
                }
                throw new UsernameNotFoundException(
                        "User " + username + " not exists");
            }
        };
    }
}

Navigate after Changing Authentication

The default Spring Security test annotations establish authentication before initial navigation. When a test changes authentication later, navigate again to apply access control to the new user. For a root route that redirects anonymous users to LoginView, use this Spring test method:

Source code
Java
@Test
@WithMockUser(username = "admin", roles = "ADMIN",
        setupBefore = TestExecutionEvent.TEST_EXECUTION)
void adminSignsInDuringTest_adminViewShown() {
    // Setup navigated to the root route while the user was still anonymous,
    // and access control redirected that navigation to the login view.
    Assertions.assertInstanceOf(LoginView.class, getCurrentView());

    // Navigating again applies access control to the current authentication.
    navigate(AdminView.class);

    Assertions.assertTrue(find(Avatar.class).single().isVisible());
}

The example uses Spring Security’s TestExecutionEvent from org.springframework.security.test.context.support. For a login-form test, perform the application’s login action and then navigate to the protected view before asserting its contents. See Authentication Timing for why reloading the current location does not replace this step.

Quarkus Security

Set Up View Access Control

To apply view access control, Vaadin requires a NavigationAccessControl to be registered as a BeforeEnterListener for the UI. Currently, the Vaadin Quarkus plugin doesn’t support automatic registration of the access control feature. To enable it for browserless testing, perform the setup in a QuarkusTestProfile class by providing an observer for the Vaadin ServiceInitEvent that executes this step.

Source code
NavigationAccessControl for Quarkus Project Test
public class TestViewSecurityConfig implements QuarkusTestProfile {

    @Override
    public String getConfigProfile() {
        return "test-security"; 1
    }

    @IfBuildProfile("test-security") 1
    public static class NavigationAccessControlInitializer {

        public void serviceInit(@Observes ServiceInitEvent event) { 2
            // @QuarkusTest starts the whole application, so we check
            // the VaadinService type to enable access control only for
            // browserless tests
            if (event.getSource() instanceof MockQuarkusServletService) { 3
                event.getSource().addUIInitListener(uiEvent -> {
                    // Customize the NavigationAccessControl as needed
                    NavigationAccessControl accessControl = new NavigationAccessControl();
                    accessControl.setLoginView(LoginView.class);

                    uiEvent.getUI().addBeforeEnterListener(accessControl);
                });
            }
        }
    }
}
  1. Sets the configuration profile to be used for the test. The class is annotated with @IfBuildProfile to make the observer only run it for tests that require this profile.

  2. Listens for Vaadin ServiceInitEvent. This is the same as implementing VaadinServiceInitListener and registering the class to be loaded by Java ServiceLoader.

  3. Checks that execution is started by the browserless test. This is required because @QuarkusTest causes the whole application to start when running the test.

Quarkus Test Security Features

When using QuarkusBrowserlessTest, if Quarkus Security is present on the classpath, the mock environment is instructed to fetch authentication details from Quarkus SecurityIdentity.

With this support, you can use Quarkus @TestSecurity annotation to simulate different authentication scenarios with test method granularity. More information is available from the Quarkus Security Testing documentation. Authentication details are available before creating the UI instance and navigating to the default route. Redirects to the login view aren’t performed when simulating logged-in users. In the same way, custom redirect logic for authenticated users works as expected.

To use Quarkus Security test annotations, first ensure the dependency is added to the project:

Source code
XML
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-test-security</artifactId>
    <scope>test</scope>
</dependency>

Next, extend QuarkusBrowserlessTest and annotate test methods to set up an authentication scenario. For the simplest situations, use @TestSecurity, providing the username and roles that should be granted.

Source code
Tests with Mock Users
@QuarkusTest
@TestProfile(TestViewSecurityConfig.class) 1
class ViewSecurityTest extends QuarkusBrowserlessTest {

    @Test
    @TestSecurity(authorizationEnabled = false) 2
    void anonymousUser_protectedView_redirectToLogin() {
        navigate("protected", LoginView.class);
    }

    @Test
    @TestSecurity(authorizationEnabled = false) 2
    void anonymousUser_publicView_signInLinkPresent() {
        // public view is default page
        Assertions.assertInstanceOf(PublicView.class, getCurrentView());

        Anchor anchor = find(Anchor.class).withText("Sign in").single();
        Assertions.assertTrue(
                test(anchor).isUsable(),
                "Sign in link should be available for anonymous user");
    }

    @Test
    @TestSecurity(user = "admin", roles = "ADMIN") 2
    void adminUser_adminView_viewShown() {
        navigate(AdminRoleView.class);

        Assertions.assertTrue(
                find(Avatar.class).single().isVisible(),
                "Avatar should be visible for logged users");
    }
}
  1. Sets a profile to activate Vaadin access control feature.

  2. Uses Quarkus test security annotations.

Verify the Suite

Run the security tests together with the rest of the suite using mvn test. For concurrent users and security isolation, see Test Multiple Users and Windows.

011585C3-F724-4B8D-B0B2-A52595587AAA

Updated