> Markdown version of [Navigation Access Control](https://vaadin.com/docs/next/flow/security/access-control/navigation-access-control). Section index: [llms.txt](https://vaadin.com/docs/next/flow/llms.txt)

# Navigation Access Control

Navigation access control decides, for every navigation, whether the current user may open the target view. It evaluates a set of pluggable rules — such as the [access annotations](https://vaadin.com/docs/next/flow/security/access-control/annotations.md) — and then either renders the view, redirects the user to the log-in view, or shows an error view.

## <a id="architecture"></a>Architecture

Navigation access control consists of three parts:

| Component                     | Role                                                                                                                                                 |
| ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `NavigationAccessControl`     | The entry point. It listens for navigation events, evaluates the security rules, and allows or denies access to the target view based on the result. |
| `NavigationAccessChecker`     | A security rule that’s evaluated during navigation. Several checkers can be active at the same time.                                                 |
| `AccessCheckDecisionResolver` | Takes the final decision to allow or deny navigation, based on the results of all checkers.                                                          |

[Image: Navigation Access Control Architecture]

`NavigationAccessControl` can be configured to evaluate multiple `NavigationAccessChecker`. For example, you can enable annotation-based view access check and path-based access check. During a navigation request, both checkers are evaluated against the current `NavigationContext` and they produce an `AccessCheckResult`.

The `AccessCheckDecisionResolver` analyzes the results and provides the final decision. The access control uses that decision to proceed with the navigation or reroute to the login view or to an error page.

### <a id="access-check-results"></a>Access Check Results

Each navigation access checker returns an `AccessCheckResult`, which holds the decision taken by the checker — and potentially an informative reason about the decision. The decision can be `ALLOW`, `DENY`, `NEUTRAL`, or `REJECT`. `ALLOW` and `DENY` are self-explanatory. `NEUTRAL` means that the checker doesn’t have enough information to make a decision, and it delegates the responsibility to the other configured checkers. `REJECT` has the same meaning of `DENY`, but it’s used to signal a critical situation where the checker cannot make the decision because of a system configuration error.

In development mode, a rejection is handled by throwing an exception so that the situation can be detected immediately. The `AccessCheckResult` object is created by calling its factory methods `allow`, `deny`, `neutral`, or `reject`. Alternatively, the same methods can be called on `NavigationContext` instance.

## <a id="built-in-access-checkers"></a>Built-in Access Checkers

Flow provides two implementations of the `NavigationAccessChecker` interface.

### <a id="annotatedviewaccesschecker"></a>AnnotatedViewAccessChecker

`AnnotatedViewAccessChecker` looks for an access annotation (`@AnonymousAllowed`, `@PermitAll`, `@RolesAllowed`, or `@DenyAll`) on the target view class. It evaluates them against the current navigation context. It always either allows or denies access, and never returns a neutral result. `AnnotatedViewAccessChecker` delegates the check to an instance of `AccessAnnotationChecker`, that can be extended to provide custom rules.

### <a id="routepathaccesschecker"></a>RoutePathAccessChecker

`RoutePathAccessChecker` evaluates the path used to navigate to a view. It requires an implementation of `AccessPathChecker`, that is responsible for the effective path verification. Vaadin’s Spring integration provides an implementation that evaluates the path against the Spring Security configuration, as described in [URL-Pattern-Based Security](https://vaadin.com/docs/next/flow/security/spring/navigation-access-control.md#url-pattern-based-security). For other security frameworks, provide your own `AccessPathChecker` implementation.

## <a id="decision-resolver"></a>Decision Resolver

The `AccessCheckDecisionResolver` component is responsible for analyzing the results provided by the navigation access checkers, and for computing the final decision to grant or deny access to a view.

The default implementation makes the decision by applying the following rules:

Table 1. Default Decision Resolver 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` |

As shown in the above table, if the navigation access checkers do not agree on the decision — excluding neutral votes — the default resolver rejects the navigation, causing an exception to be thrown in development mode. This situation happens usually because of invalid security configurations. For example, a view may be annotated with `@AnonymousAllowed`, but the Spring Security configuration has a request matcher for the view path that grants access only to an authenticated user.

In error handling phase, almost the same rule applies with the exception that if all the results are `NEUTRAL`, the access is granted. The reason is that, in this case, the target of the navigation is supposed to be an error handler component that do not expose sensible information.

Table 2. Default Decision Resolver Rules For Error Handling Phase

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

## <a id="configuration"></a>Configuration

In a Spring application, `VaadinSecurityConfigurer` enables navigation access control automatically. See [Customizing Navigation Access Control](https://vaadin.com/docs/next/flow/security/spring/navigation-access-control.md) for how to customize it there, including URL-pattern-based security. In a plain servlet application, a `VaadinServiceInitListener` enables it, as described in [Setting Up Security](https://vaadin.com/docs/next/flow/security/plain-servlet/setup.md#adding-vaadinserviceinitlistener).

## <a id="customization"></a>Customization

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

If the built-in checkers aren’t enough, a custom `NavigationAccessChecker` can be implemented. `NavigationAccessChecker` has a single method that takes a `NavigationContext` as input and produces an `AccessCheckResult` as output.

The `NavigationContext` provides information about the current navigation, such as target view class and location, potential authenticated user and its roles.

For example, a navigation access check implementation that allows access to a voting view only when voting has been marked as open, could look like the following code:

`VotingNavigationAccessChecker.java`

```java
public class VotingNavigationAccessChecker implements NavigationAccessChecker {

    @Override
    public AccessCheckResult check(NavigationContext context) {
        AccessCheckResult result;
        if (VotingView.class.equals(context.getNavigationTarget())) {
            if (context.getParameters().getParameterNames()
                    .contains("eventId")) {
                // Allow access only if the event the user is going to express
                // its vote is actually open for voting, otherwise deny it.
                result = context.getParameters().getInteger("eventId")
                        .filter(this::isVotingOpen)
                        .map(unused -> AccessCheckResult.allow()).orElseGet(
                                () -> AccessCheckResult.deny("Voting closed"));
            } else {
                // Critical error, the navigation does not carry a required
                // information. Probably a misconfigured route annotation or
                // a broken link in another view
                result = AccessCheckResult
                        .reject("Event identifier not provided");
            }
        } else {
            // Not a navigation to voting view, let other checkers take the
            // decision
            result = AccessCheckResult.neutral();
        }
        return result;
    }

    private boolean isVotingOpen(int eventId) {
        // Fetch the event from data storage and check if voting is currently
        // opened (implementation omitted in this example)
        return false;
    }
}
```

### <a id="navigation-error-handling-phase"></a>Navigation Error Handling Phase

When implementing a custom `NavigationAccessChecker`, it’s important to know that the checker is called also when the navigation is rerouted to an error handler view. The `NavigationContext` class provides the `isErrorHandling()` method to verify if the call happens during a navigation error handling phase. In this case, a custom checker would probably abstain from making a decision for the internal navigation, by returning a `NEUTRAL` result.

Neutral Result on Error Handling Phase

```java
@Override
public AccessCheckResult check(NavigationContext context) {
    if (context.isErrorHandling()) {
        return AccessCheckResult.neutral();
    }
    ....
}
```

### <a id="custom-decision-resolver"></a>Custom Decision Resolver

If the default implementation doesn’t fit the requirements of the project, you can implement your own `AccessCheckDecisionResolver`. The interface defines a single method that takes as input the results computed by the navigation access checker and the current navigation context. Its output is an `AccessCheckResult`.

In the following example, the decision resolver allows access to the view if at least one of the checkers provided an `ALLOW` result:

`CustomDecisionResolver.java`

```java
public class CustomDecisionResolver implements AccessCheckDecisionResolver {
    @Override
    public AccessCheckResult resolve(List<AccessCheckResult> results,
            NavigationContext context) {
        if (results.stream()
                .anyMatch(r -> r.decision() == AccessCheckDecision.ALLOW)) {
            return AccessCheckResult.allow();
        }
        return AccessCheckResult.deny("Access denied");
    }
}
```

`4164EB30-201D-4FD3-940F-03630A752AD5`
