> Markdown version of [Flow](https://vaadin.com/docs/v24/building-apps/security/protect-views/flow). Section index: [llms.txt](https://vaadin.com/docs/v24/building-apps/llms.txt)

# Protect Flow Views

In this guide, you’ll learn how to control access to specific views both declaratively and programmatically. A hands-on mini-tutorial at the end will help you apply these concepts in a real Vaadin application.

## <a id="declarative-view-security"></a>Declarative View Security

The easiest way to grant or deny access to a Flow view is to use annotations. The following annotations are supported:

- `@AnonymousAllowed` allows access to **unauthenticated** users.

- `@PermitAll` allows any **authenticated** user to navigate to the view.

- `@RolesAllowed` allows users **having the roles** specified in the annotation value to navigate to the view.

- `@DenyAll` prevents **everyone** from navigating to the view. By default, **all Flow views are inaccessible unless explicitly annotated**.

> **Note:** The `@AnonymousAllowed` annotation is a Vaadin-specific annotation; the others are Jakarta annotations (JSR-250). The Spring Security annotations `@Secured` and `@PreAuthorize` are **not supported on views**.

The following example uses `@AnonymousAllowed` to allow **all users** — both authenticated and unauthenticated — to access the view:

```java
@Route("public")
@PageTitle("Public View")
// tag::snippet[]
@AnonymousAllowed
// end::snippet[]
public class PublicView extends Main {
    ...
}
```

In business applications that require authentication for everything, you typically have to do this on the login view.

The next example uses `@PermitAll` to allow **only authenticated users** — with any role — to access the view:

```java
@Route("private")
@PageTitle("Private View")
// tag::snippet[]
@PermitAll
// end::snippet[]
public class PrivateView extends Main {
    ...
}
```

This example uses `@RolesAllowed` to allow **users with the `ADMIN` role** to navigate to the view:

```java
@Route("admin")
@PageTitle("Admin View")
// tag::snippet[]
@RolesAllowed("ADMIN")
// end::snippet[]
public class AdminView extends Main {
    ...
}
```

> **Important: Router layouts are also protected**
>
> When protecting views, ensure the router layout also allows access. If a view is accessible but its parent layout is restricted, users will still be blocked.

### <a id="annotation-inheritance"></a>Annotation Inheritance

Security annotations are inherited from the closest superclass that has them. Annotating a subclass overrides any inherited annotations. Interfaces aren’t checked for annotations, only classes.

In the following example, `UserListingView` requires the `ADMIN` role:

```java
@RolesAllowed("ADMIN")
public abstract class AbstractAdminView extends VerticalLayout {
    ...
}

@Route(value = "user-listing")
public class UserListingView extends AbstractAdminView {
    ...
}
```

While multiple security annotations can be applied to a single view, doing so can cause conflicts and is **not recommended**. However, if multiple annotations exist on a view, they override each other in this order:

- `@DenyAll` overrides all other annotations;

- `@AnonymousAllowed` overrides `@RolesAllowed` and `@PermitAll`; and

- `@RolesAllowed` overrides `@PermitAll`.

## <a id="programmatic-view-security"></a>Programmatic View Security

Vaadin provides API:s for protecting views programmatically. It’s more verbose than using the annotations, but gives you greater control.

### <a id="making-a-custom-navigation-access-checker"></a>Making a Custom Navigation Access Checker

If you need **more fine-grained control** over how access is granted to views, you can implement a custom `NavigationAccessChecker`:

```java
@Component // (1)
class CustomAccessChecker implements NavigationAccessChecker {

    @Override
    public AccessCheckResult check(NavigationContext context) {
        // Check whether to allow or deny access
    }
}
```

1. Registers the navigation access checker as a singleton Spring bean.

The `NavigationContext` object contains information about where you’re trying to navigate, such as the view class, route parameters, and query parameters. It also contains the principal of the current user.

Since the access checker is a Spring bean, you inject other beans into it. For example, you may want to lookup additional information in order to make the access decision.

Once you’ve made a decision, you have to return an `AccessCheckResult`. The `AccessCheckResult` determines whether navigation is allowed, denied, or deferred. There are four possible outcomes:

- `AccessCheckResult.allow()`

  Access is granted.

- `AccessCheckResult.neutral()`

  The access checker cannot make a decision based on the given navigation information. Another access checker have to make the decision, or access will be denied.

- `AccessCheckResult.deny()`

  Access is denied.

- `AccessCheckResult.reject()`

  Access is denied because of a misconfiguration or critical development time error.

> **Note:** The security annotations are actually enforced by a built-in access checker.

### <a id="enabling-a-navigation-access-checker"></a>Enabling a Navigation Access Checker

To enable a custom `NavigationAccessChecker`, create a new `NavigationAccessControlConfigurer` Spring bean:

`SecurityConfig.java`

```java
@EnableWebSecurity
@Configuration
@Import(VaadinAwareSecurityContextHolderStrategyConfiguration.class)
class SecurityConfig {

    @Bean
    static NavigationAccessControlConfigurer navigationAccessControlConfigurer( // (1)
            CustomAccessChecker customAccessChecker) {
        return new NavigationAccessControlConfigurer()
                .withNavigationAccessChecker(customAccessChecker); // (2)
    }
    ...
}
```

1. The `@Bean` method must be `static` to prevent bootstrap errors caused by circular dependencies in bean definitions.

2. `CustomAccessChecker` is now **the only enabled access checker**.

You can have multiple access checkers active at the same time. When you navigate to a view, they will all be consulted.

> **Note:** To enable the built-in annotated view access checker, call `NavigationAccessControlConfigurer.withAnnotatedViewAccessChecker()`.

An `AccessCheckDecisionResolver` computes the final access decision based on the results of every access checker. The default implementation makes the decision by applying the following rules:

| Navigation Access Checkers Results | Decision |
| ---------------------------------- | -------- |
| `ALL ALLOW`                        | `ALLOW`  |
| `ALLOW + NEUTRAL`                  | `ALLOW`  |
| `ALL DENY`                         | `DENY`   |
| `DENY + NEUTRAL`                   | `DENY`   |
| `ALL NEUTRAL`                      | `DENY`   |
| `ALLOW + DENY`                     | `REJECT` |
| `ALLOW + DENY + NEUTRAL`           | `REJECT` |

> **Important:** The built-in annotated view access checker never returns `NEUTRAL`. It either grants or denies access.

By default, having access checkers both allowing and denying access at the same time is considered a configuration error. This can happen if you combine the built-in annotated view access checker with a custom access checker. If this is what you want, you have to create a custom `AccessCheckDecisionResolver`. This, and more, is covered in the [Navigation Access Control Reference Guide](https://vaadin.com/docs/v24/flow/security/advanced-topics/navigation-access-control.md).

### <a id="controlling-access-within-views"></a>Controlling Access within Views

Sometimes, you want multiple roles to be able to access a view, but limit what they can do within it. For instance, one role may have full read-write access whereas another role has only read-only access. **To check the roles of the current user**, inject an `AuthenticationContext` object into your view:

```java
@Route("")
@PermitAll // (1)
public class MyView extends Main {

    public MyView(AuthenticationContext authenticationContext) {
        if (authenticationContext.hasRole("ADMIN")) { // (2)
            // Set up the UI for an admin
        } else {
            // Set up the UI for normal users
        }
    }
    ...
}
```

1. All authenticated user have access to the view.

2. Administrators can do more inside the view than normal users.

`AuthenticationContext` has multiple methods for checking the roles and authorities of the current user. Refer to the Javadoc for more information.

## <a id="role-constants"></a>Role Constants

To reduce the risk of typos, consider **defining all your application’s roles as constants**. In the `security` package, make a `Roles` class:

Roles.java

```java
public final class Roles {
    public static final String ADMIN = "ADMIN";
    public static final String USER = "USER";

    private Roles() {}
}
```

Then refer to the constants in annotations and method calls:

```java
@Route
// tag::snippet[]
@RolesAllowed(Roles.ADMIN)
// end::snippet[]
public class AdminView extends Main {
    ...
}
```

```java
@Route("")
@PermitAll
public class MyView extends Main {

    public MyView(AuthenticationContext authenticationContext) {
// tag::snippet[]
        if (authenticationContext.hasRole(Roles.ADMIN)) {
// end::snippet[]
            // Set up the UI for an admin
        } else {
            // Set up the UI for normal users
        }
    }
    ...
}
```

## <a id="try-it"></a>Try It

In this mini-tutorial, you’ll learn how to add declarative view security to a real Vaadin application. You’ll also learn how to control access programmatically inside a view. The tutorial uses the project from the [Add Logout](https://vaadin.com/docs/v24/building-apps/security/add-logout/flow.md#try-it) guide. If you haven’t completed that tutorial yet, do it now before proceeding.

Create Role Constants

Create a new class `Roles` in the `[application package].security` package:

Roles.java

```java
public final class Roles {
    public static final String ADMIN = "ADMIN";
    public static final String USER = "USER";

    private Roles() {
    }
}
```

Then update the `userDetailsManager()` method of the `SecurityConfig` class to use the new constants:

`SecurityConfig.java`

```java
@EnableWebSecurity
@Configuration
@Import(VaadinAwareSecurityContextHolderStrategyConfiguration.class)
class SecurityConfig {
    ...

    @Bean
    public UserDetailsManager userDetailsManager() {
        LoggerFactory.getLogger(SecurityConfig.class)
            .warn("Using in-memory user details manager!");
        var user = User.withUsername("user")
                .password("{noop}user")
// tag::snippet[]
                .roles(Roles.USER)
// end::snippet[]
                .build();
        var admin = User.withUsername("admin")
                .password("{noop}admin")
// tag::snippet[]
                .roles(Roles.ADMIN)
// end::snippet[]
                .build();
        return new InMemoryUserDetailsManager(user, admin);
    }
}
```

Create Admin View

Create a new class `AdminView` in the `[application package].examplefeature.ui` package:

AdminView\.java

```java
import com.example.application.security.Roles; // (1)

import com.vaadin.flow.component.html.Main;
import com.vaadin.flow.router.Menu;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import jakarta.annotation.security.RolesAllowed;

@Route
@PageTitle("Task Admin")
@Menu(order = 10, icon = "vaadin:wrench", title = "Task Admin")
@RolesAllowed(Roles.ADMIN)
public class AdminView extends Main {

    public AdminView() {
        setText("Admin View");
    }
}
```

1. Replace with real package.

Now navigate to: <http://localhost:8080>

Log in as an `ADMIN`. You should see **Task Admin** in the navigation menu. Clicking it should take you to the admin view.

Now log out and log back in as a `USER`. The **Task Admin** menu item should no longer be visible.

Attempt to access <http://localhost:8080/admin> directly. You should see an access denied error.

Make the Task List Read-Only For Users

So far all authenticated users have been able to add tasks to `TaskListView`. You’ll now change it so that only users with the `ADMIN` role can add tasks. Open `TaskListView` and change the constructor as follows:

TaskListView\.java

```java
@Route("")
@PageTitle("Task List")
@Menu(order = 0, icon = "vaadin:clipboard-check", title = "Task List")
@PermitAll
public class TaskListView extends Main {

    public TaskListView(TaskService taskService,
// tag::snippet[]
            AuthenticationContext authenticationContext) {
// end::snippet[]

        // The rest of the constructor omitted

// tag::snippet[]
        if (authenticationContext.hasRole(Roles.ADMIN)) {
            add(new ViewToolbar("Task List",
                ViewToolbar.group(description, dueDate, createBtn))); // (1)
        } else {
            add(new ViewToolbar("Task List")); // (2)
        }
// end::snippet[]
        add(taskGrid);
    }
    ...
}
```

1. Only create the toolbar if the user is an `ADMIN`.

Go back to your browser and try the application. The toolbar should only be visible in the **Task List** when you are logged in as `ADMIN`.

> **Note:** The `description`, `dueDate`, and `createBtn` components are created for all users, even though they are only used by administrators. Technically this is a waste of memory, but this time it is acceptable since the view is so small.

Final Thoughts

Now that your views are secure, the next step is to **protect application services**. Learn how in the [Protect Services](https://vaadin.com/docs/v24/building-apps/security/protect-services.md) guide.
