Skip to content
🇪🇺 Made in the European Union · Independently built · Released under EUPL 1.2
Vaadin Integration

Vaadin Integration

To secure a Vaadin Flow application, implement the SPI contracts below and register them via META-INF/services/ files. The demo-vaadin module is the reference implementation.

1. Define a user type

public record MyUser(String username, Set<String> roles) {}

2. Implement AuthenticationService<T, U>

Validates credentials and loads the user subject.

public class MyAuthenticationService
    implements AuthenticationService<Credentials, MyUser> {

  @Override
  public boolean checkCredentials(Credentials credentials) { /* ... */ }

  @Override
  public MyUser loadSubject(Credentials credentials) { /* ... */ }

  @Override
  public Class<MyUser> subjectType() { return MyUser.class; }
}

Register in META-INF/services/com.svenruppert.vaadin.security.authorization.api.AuthenticationService:

com.example.MyAuthenticationService

3. Implement AuthorizationService<U>

Maps a user to roles. Only rolesFor() is required — permissionsFor() has a default implementation returning empty permissions.

public class MyAuthorizationService implements AuthorizationService<MyUser> {
  @Override
  public HasRoles rolesFor(MyUser subject) { /* ... */ }
}

Register in META-INF/services/com.svenruppert.vaadin.security.authorization.api.AuthorizationService.

4. Define a restriction annotation with @SecurityAnnotation

@Retention(RUNTIME)
@SecurityAnnotation(MyRoleAccessEvaluator.class)
public @interface VisibleFor {
  MyRole[] value();
}

Or use the generic annotations from security-core:

@RequiresRole("ROLE_ADMIN")
@RequiresPermission("demo:edit")

5. Implement AccessEvaluator

public class MyRoleAccessEvaluator
    implements AccessEvaluator<VisibleFor> {

  @Override
  public AccessDecision evaluate(AccessContext context, VisibleFor annotation) {
    // return AccessDecision.granted() or AccessDecision.denied("login", false)
  }
}

Or extend RoleBasedAccessEvaluator:

public class MyRoleAccessEvaluator
    extends RoleBasedAccessEvaluator<VisibleFor, MyUser> {

  @Override
  public Set<RoleName> requiredRoles(VisibleFor annotation) { /* ... */ }

  @Override
  public String alternativeNavigationTarget(
      AccessContext context, VisibleFor annotation) { /* ... */ }
}

Register in META-INF/services/com.svenruppert.vaadin.security.authorization.api.AccessEvaluator.

6. Extend LoginListener<U>

public class MyLoginListener extends LoginListener<MyUser> {
  @Override
  public Class<? extends LoginView> loginNavigationTarget() {
    return MyLoginView.class;
  }
  @Override
  public Class<? extends Component> defaultNavigationTarget() {
    return MainView.class;
  }
}

Register in META-INF/services/com.svenruppert.vaadin.security.authorization.LoginListener.

7. Extend LoginView

Create your login UI by extending the abstract LoginView base class.

8. Annotate route views

@Route("admin")
@VisibleFor(MyRole.ADMIN)
public class AdminView extends Div { /* ... */ }

How the Vaadin adapter behaves

  1. Detects security annotations before navigation.
  2. Checks whether a subject is present.
  3. Redirects unauthenticated users to the login view.
  4. Runs the matching evaluator.
  5. Maps the resulting decision to Vaadin navigation operations.

Hiding buttons or menu entries is only a usability measure. The actual protection boundary is server-side navigation and service-level authorization.