Skip to content

Restricting Access to AppJar Views

AppJars do not decide which users may open their views. That decision belongs to the host application, which knows its own roles and its own security model. This page explains the access policy shared by all AppJars and how an application restricts their views to specific roles.

The Access Policy

Apart from the exceptions listed below, every view an AppJar provides is annotated with @PermitAll. Under Vaadin's default navigation access control, this means that any authenticated user can open it, and that anonymous users are sent to the login view. No AppJar view requires a particular role out of the box, including the administration views.

The exceptions are:

  • User Manager: the user registration and password reset views are annotated with @AnonymousAllowed, because whoever opens them has no account yet or cannot sign in. User Manager also declares their paths public in Spring Security. See Public paths declared by the appjar.
  • Email Manager: in version 2.1.0, the Templates view (TemplateCrudView) has no access annotation, so annotation-based access control denies it to every user until the application grants access to its route. Under the route path checker described in Applying the Rules to In-App Navigation, it follows the request matchers like any other view. See Add the Views to the Navigation.

Restricting a view to administrators, or to any other role, is therefore part of integrating an AppJar.

Matching AppJar Routes in Spring Security

The recommended place to restrict AppJar views is the application's SecurityFilterChain. Each AppJar registers its views under a common path prefix, such as al/ for Activity Log or um/ for User Manager, so a few request matchers cover every view. The default paths and the properties that change them are listed in Configuring View Route Paths.

The following configuration restricts the administration views of several AppJars to the ADMIN role, while leaving the views intended for every user, such as My Configurations and My Profile, open to any authenticated user:

@EnableWebSecurity
@Configuration
public class SecurityConfiguration {

    @Bean
    static NavigationAccessControlConfigurer navigationAccessControlConfigurer() {
        return new NavigationAccessControlConfigurer()
            .withRoutePathAccessChecker();
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        // Rules are first-match in declaration order, and vaadin() appends a final
        // anyRequest matcher, so every rule must be declared before calling it.
        http.authorizeHttpRequests(authorize -> authorize
            .requestMatchers("/login*").permitAll()
            // Views open to every authenticated user
            .requestMatchers("/cfg/user/my-configs", "/up/profile").authenticated()
            // Administration views
            .requestMatchers(
                "/al/**",
                "/cfg/**",
                "/dm/**",
                "/em/**",
                "/i18n/**",
                "/pm/**",
                "/up/profile-list")
                .hasRole("ADMIN"));

        http.with(vaadin(), configurer -> configurer.loginView(LoginView.class));

        return http.build();
    }
}

The vaadin() method is a static import from com.vaadin.flow.spring.security.VaadinSecurityConfigurer. The NavigationAccessControlConfigurer class is in the com.vaadin.flow.spring.security package.

Vaadin 25 removed the VaadinWebSecurity base class used by earlier versions, so a configuration that extends it, or that overrides its configure(HttpSecurity) method, no longer compiles. Declare a SecurityFilterChain bean as shown above instead.

Keep the following points in mind when writing the matchers:

  • Declaration order: authorizeHttpRequests rules are first-match. Declare the narrower matchers, such as /cfg/user/my-configs, before the broader ones, such as /cfg/**, and declare every rule before http.with(vaadin(), …).
  • Route paths: the matchers must follow the paths the AppJar views are actually registered under. An application that changes a route path through its properties must update the corresponding matcher, otherwise the view is no longer covered by it.
  • Route parameters: a matcher that ends in /** also covers the subpaths an AppJar uses for create, edit and parameterised routes, such as al/extractors/edit or i18n/items/:lang.

Applying the Rules to In-App Navigation

Request matchers are evaluated when the browser requests a URL from the server: when the user opens a view by its address, reloads the page or follows a bookmark. Navigating between views inside a running Vaadin application, for example by clicking a menu item, does not issue such a request, so Spring Security alone does not see it. That navigation is evaluated by Vaadin's navigation access control instead, which by default only reads the view annotations, and every AppJar view allows any authenticated user.

The NavigationAccessControlConfigurer bean in the example above closes this gap. It enables RoutePathAccessChecker, which evaluates the path of every navigation against the same Spring Security request matchers, so a single set of rules governs both direct requests and in-app navigation. Declare the bean method static when it lives in the same class as the SecurityFilterChain, to avoid a circular bean definition.

Exposing this bean replaces Vaadin's default checkers, so the configuration above enables only the route path checker and view annotations are no longer consulted. An application that also relies on annotations on its own views can add .withAnnotatedViewAccessChecker(). In that case, when the two checkers disagree about a view, for example a @PermitAll AppJar view whose path is restricted to ADMIN, Vaadin's default decision resolver rejects the navigation, and in development mode it reports the conflict as an error.

Applications Using User Manager

User Manager requires its own NavigationAccessControlConfigurer bean, which activates UserManagerNavigationAccessChecker, as described in Configure Spring Security. That checker evaluates the access rules administrators configure at runtime and, for any view no rule covers, delegates to a fallback checker that reads the view annotations.

An application that uses User Manager has two ways to restrict AppJar views:

  • Access rules: an administrator creates rules that grant the AppJar routes to specific roles, without changing code. See Access Rules.
  • Request matchers: the application declares the matchers shown above in its SecurityFilterChain and replaces the fallback checker with a RoutePathAccessChecker, so that in-app navigation to views no access rule covers is evaluated against the same matchers. See The fallback checker.

Hiding Menu Items

Restricting a view does not remove its entry from the navigation menu. The Getting Started guides guard each menu item with accessChecker.hasAccess(…). When accessChecker is an AccessAnnotationChecker, it reads only the view annotations, so it reports every @PermitAll AppJar view as accessible to any authenticated user. An application that restricts views through request matchers should guard the corresponding menu items with the same role, for example with Vaadin's AuthenticationContext:

if (authenticationContext.hasRole("ADMIN")) {
    nav.addItem(new SideNavItem("Activity Log", ActivityLogView.class, VaadinIcon.LIST.create()));
}

In an application that uses Dynamic Menu, the visibility of each menu item is restricted by role in the menu item editor instead. See Menu Items.