Docs

Documentation versions (currently viewingVaadin 24)

Protect Views

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

Client Security

In a Hilla view, the code is running in the browser and could easily be manipulated using the browser’s development tools. Client-side security enhances user experience by hiding inaccessible features and views, but it does not provide real security.

Warning
When working with Hilla views, the real security layer protects the browser callable services. For more information about this, see the Protect Services guide.

Requiring Authentication

By default, all Hilla views are accessible without authentication. To require login, add loginRequired: true to the view configuration. This ensures that users must authenticate before accessing the view:

export const config: ViewConfig = {
    title: 'Protected View',
    menu: {
        icon: 'vaadin:safe-lock',
        order: 1,
        title: 'Protected View',
    },
    loginRequired: true
}

export default function ProtectedView() {
    return (
        <main>
            This view requires login
        </main>
    );
}
Important
The ViewConfig object is parsed on the server as JSON. Because of this, you cannot use constants or TypeScript code in it.

If you want all views to require login, set the loginRequired attribute on the main layout:

export const config: ViewConfig = {
    loginRequired: true
}

export default function MainLayout() {
    return (
        <AppLayout primarySection="drawer">
            <Header/>
            <Scroller slot="drawer">
                <MainMenu/>
            </Scroller>
            <UserMenu/>
            <Suspense fallback={<ProgressBar indeterminate={true} className="m-0"/>}>
                <Outlet/>
            </Suspense>
        </AppLayout>
    );
}

Now all views rendered inside the layout require authentication.

Role-Based Access

Vaadin supports granting access to views based on the roles of the user. To do this, you have to first make the roles of the current user available to the clients-side authentication context. Next, you have to declare which role or roles are required to access a certain view or router layout.

Adding Roles to Authentication Context

When you enabled authentication, you created an auth.ts file that setup the client authentication context. To use role-based security, you have to change the setup with information about the current user’s roles. Pass a second argument to the configureAuth helper function:

import { configureAuth } from '@vaadin/hilla-react-auth';
import { UserInfoService } from "Frontend/generated/endpoints";

const auth = configureAuth(UserInfoService.getUserInfo, {
    getRoles: (user) => { 1
        return user.authorities
            .filter(s => s.startsWith("ROLE_")) 2
            .map(s => s.substring(5)); 3
    }
})
export const useAuth = auth.useAuth
export const AuthProvider = auth.AuthProvider
  1. The user object is of the type returned by UserInfoService.getUserInfo().

  2. Includes only role authorities. In Spring Security, roles start with the ROLE_ prefix by default.

  3. Strips the ROLE_ prefix to get only the name of the role.

Declaring Required Roles

To make a view or layout available only to users with a specific role or roles, declare the roles in the configuration object:

export const config: ViewConfig = {
    loginRequired: true,
    rolesAllowed: [ "ADMIN" ], 1
    ...
}

export default function AdminView() {
    ...
}
  1. The rolesAllowed attribute is an array of strings.

Checking Access Programmatically

Sometimes, you may want to control which actions a user can perform inside a view. Instead of blocking access entirely, you can conditionally render elements based on the user’s role. 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, use the useAuth hook:

import { useAuth } from "Frontend/security/auth";

export default function MyView() {
    const auth = useAuth();
    const isAdmin = auth.hasAccess({
        rolesAllowed: [ "ADMIN" ] 1
    });

    return (
        <main>
            {isAdmin && <p>Only admins see this</p>}
            <p>Everyone can see this</p>
        </main>
    );
}
  1. The rolesAllowed attribute is an array of strings.

Try It

In this mini-tutorial, you’ll learn how to control access to Hilla views both declaratively and programmatically. It uses the project from the Add Logout guide. If you haven’t completed that tutorial yet, do it now before proceeding.

Add Roles to Authentication Context

Open src/main/frontend/security/auth.ts and make the following changes:

import { configureAuth } from '@vaadin/hilla-react-auth';
import { UserInfoService } from "Frontend/generated/endpoints";

const auth = configureAuth(UserInfoService.getUserInfo, {
    getRoles: (user) => {
        return user.authorities
            .filter(s => s.startsWith("ROLE_"))
            .map(s => s.substring(5));
    }
})
export const useAuth = auth.useAuth
export const AuthProvider = auth.AuthProvider
Create Admin View

Create a new file src/main/frontend/views/admin.tsx:

import { ViewConfig } from "@vaadin/hilla-file-router/types.js";

export const config: ViewConfig = {
    title: "Task Admin",
    menu: {
        title: "Task Admin",
        order: 10,
        icon: "vaadin:wrench",
    },
    rolesAllowed: [
        "ADMIN"
    ],
}

export default function AdminView() {
    return (
        <main>
            Admin View
        </main>
    );
}

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 end up on the login view again.

Make the Task List Read-Only For Users

So far all authenticated users have been able to add tasks to the todo view. You’ll now change it so that only users with the ADMIN role can add tasks. Open src/main/frontend/views/index.tsx and change it as follows:

import { useAuth } from "Frontend/security/auth";
...
export default function TodoView() {
  const dataProvider = useDataProvider<Todo>({
    list: (pageable) => TodoService.list(pageable),
  });
  const auth = useAuth();
  const isAdmin = auth.hasAccess({ rolesAllowed: ["ADMIN"] });

  return (
    <main className="w-full h-full flex flex-col box-border gap-s p-m">
      <ViewToolbar title="Task List">
        {isAdmin && <Group> 1
          <TodoEntryForm onTodoCreated={dataProvider.refresh} />
        </Group>}
      </ViewToolbar>
      <Grid dataProvider={dataProvider.dataProvider}>
        <GridColumn path="description" />
        <GridColumn path="dueDate" header="DueDate">
          {({ item }) => (item.dueDate ? dateFormatter.format(new Date(item.dueDate)) : 'Never')}
        </GridColumn>
        <GridColumn path="creationDate" header="Creation Date">
          {({ item }) => dateTimeFormatter.format(new Date(item.creationDate))}
        </GridColumn>
      </Grid>
    </main>
  );
}
  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.

Final Thoughts

These techniques only improve the user experience and do not provide real security. Always secure your backend services to enforce true access control. Learn more in the Protect Services guide.