Getting started with SSO Kit

Step-by-step guide on how to use SSO Kit in an application.

SSO Kit builds upon Spring Boot and Spring Security. It comes with a starter module that configures the security settings needed to authenticate with an identity provider.

1. Create a Hilla application without authentication

You can create a Hilla application by entering the following from the command-line:

npx @hilla/cli init <your-project-name>

2. Backend

Once you’ve created the Hilla application, you can begin securing it by installing and configuring SSO Kit on the backend. The following section shows how to block unauthorized users from using a button in an application. You can install and update SSO Kit by adding it as a dependency to your application in the pom.xml file.

2.1. Add the SSO Kit dependency

Add the sso-kit-starter module and other required dependencies to the pom.xml of a Vaadin application like so:


Check the SSO Kit releases page for the latest version, or a different version of the sso-kit-starter dependency.

2.2. Configure endpoints

For Hilla to be able to parse and generate endpoints from an application and from SSO Kit for your frontend, you have to add a configuration to the Hilla Maven plugin with your application and SSO Kit package names like this:

    <!-- Add this configuration -->
    <!-- ... -->

By default, the package name in generated Hilla projects is com.example.application. SSO Kit uses the package name dev.hilla.sso.starter. To be able to find the annotated endpoints in SSO Kit that are required, you have to whitelist its package name in your Spring Boot application. To do this, open your and add these packages to the @SpringBootApplication annotation like so:

@SpringBootApplication(scanBasePackages = {
  "com.example.application", // Application package
  "dev.hilla.sso.starter" // SSO Kit package
public class Application // ...

2.3. Configure the SSO provider in Spring

Next, you need to set some configuration properties to connect SSO Kit to your OpenID Connect provider. These properties can be added to your file where you give the provider URL and the client registration details, such as credentials and scope.

Provider definition is configured within the namespace where you give a key to identify your provider, such as keycloak. You can use the same key to register the client for that provider within the namespace, where you specify client credentials and the requested scope.

The scope is a list of keywords to request the provider for a specific set of information, such as user profile, email or roles. The following is an example of the properties to set to enable a Keycloak instance to perform authentication:

# Customize the following property values for your Keycloak configuration:

2.4. Secure the application

A Hilla application includes front-end code and back-end endpoints. Both of them can and should benefit from the authentication protection.

2.4.1. Protect the example endpoint

Hilla allows fine-grained authorization on endpoints and endpoint methods. You can use annotations like @PermitAll or @RolesAllowed(…​) to declare who can access what.

To try this feature, replace the @AnonymousAllowed annotation in with @PermitAll, so that unauthenticated users will be unable to access all endpoint methods. You could also apply the same annotation at the method level for more fine-grained control.

Start the application using the ./mvnw command (.\mvnw on Windows). Then try the application in the browser. It should work correctly, except that when you click on the Say hello button, nothing happens. This is because the endpoint is no longer accessible without authentication.

3. Frontend

Once the backend is secure, you can begin extending authentication features to the frontend. The following section shows how to display user information (e.g., a name) on secured views and enable users to log in and out.

3.1. Implement authentication state

All of the essential authentication state is already available in a global variable and can be added to the application state, which is inside the AppStore class in app-store.ts:

import SingleSignOnData from 'Frontend/generated/dev/hilla/sso/starter/SingleSignOnData';

// All necessary data is already loaded in the Hilla global variable
authInfo = (window as any).Hilla.SSO as SingleSignOnData;

3.2. Add log-in and log-out buttons

As an example, add two buttons to the drawer footer — one to sign in, and another to sign out. When signing out, it’s important to invoke the logout function provided by Hilla to perform logout on the server. Then, load the SSO provider logout page.

import { logout } from '@hilla/frontend';

// Replace the `footer` in the rendered `html`
<footer slot="drawer">
    ? html`<vaadin-button @click="${this.signOut}">Sign out</vaadin-button>`
    : html`<vaadin-button @click="${this.signIn}">Sign in</vaadin-button>`

// Add the needed functions inside the class
private signOut = async () => {
  await logout(); // Logout on the server
  location.href = appStore.authInfo.logoutLink!;

private signIn = () => {
  location.href = appStore.authInfo.loginLink;

3.3. Add access control

You can protect your views by verifying that each authentication has happened before loading the view.

In app-store.ts, add a new type definition and a function to check access rights using that type:

export type AccessProps = {
  requiresLogin?: boolean;

// Put this function inside the AppStore class
hasAccess = (route: AccessProps) => {
  return !route.requiresLogin || this.authInfo.authenticated;

In the frontend/routes.ts file, use the AccessProps type and protect the About view:

import { AccessProps } from './stores/app-store';

// Add AccessProps to the ViewRoute type
export type ViewRoute = Route & AccessProps & {
  // ...

// Add the requiresLogin attribute to the About view
  path: 'about',
  // ...
  requiresLogin: true,

Then, filter the menu excluding unauthorized views by amending the view filter in main-layout.ts:

// Add a new condition in getMenuRoutes that checks for authentication
private getMenuRoutes(): RouteInfo[] {
  return views.filter((route) => route.title).filter(appStore.hasAccess) as RouteInfo[];

Now the About item in the menu appears only when authenticated.

3.4. Show user information

The SSO Kit provides a default endpoint to get information about the authenticated user. You can implement yours if you want to customize the returned object and its fields.

As the About page is now protected, that’s a perfect place to show some information about the current user:

import User from 'Frontend/generated/dev/hilla/sso/starter/endpoint/User';
import { UserEndpoint } from 'Frontend/generated/endpoints';
import { property } from 'lit/decorators.js';

// Add a property for the user
user: User | undefined;

// Add the keyword `async` to connectedCallback and then load the user inside the function
async connectedCallback() {
  // ...
  this.user = await UserEndpoint.getAuthenticatedUser();

// Add some output
<p>Username: ${this.user?.preferredUsername}</p>
<p>Full name: ${this.user?.fullName}</p>
<p>Email: ${this.user?.email}</p>

4. Single sign-off

SSO Kit provides two methods for logging out the user. They’re defined by the OpenID Connect specification like so:

4.1. RP-initiated logout

RP-initiated logout (i.e., Relaying Party, the application) enables the user to logout from the application itself, ensuring the connected provider session is terminated.

4.2. Back-channel logout

Back-Channel Logout is a feature that enables the provider to close user sessions from outside the application. For example, it can be done from the provider’s user dashboard or from another application.

4.2.1. Enable the feature

To enable the feature in the application, you need to set the hilla.sso.back-channel-logout property to true. You would do this like you see here:


The client should then be configured on the provider’s dashboard to send logout requests to a specific application URL: /logout/back-channel/{registration-key}, where {registration-key} is the provider key.

4.2.2. Modify the frontend

State about back-channel logout can be added to app-store.ts:

import { BackChannelLogoutEndpoint } from 'Frontend/generated/endpoints';

// Will become true when back-channel logout happens
backChannelLogoutHappened = false;

constructor() {

  // Add this to the constructor to subscribe to back-channel logout events
  if (this.authInfo.backChannelLogoutEnabled) {
    const subscription = BackChannelLogoutEndpoint.subscribe();
    subscription.onNext(() => {
      this.backChannelLogoutHappened = true;

// Clears authInfo without reloading the page
clearAuthInfo() {
  this.authInfo = {
      ... this.authInfo,
      authenticated: false,
      backChannelLogoutEnabled: false,
      logoutLink: undefined,
      roles: [],

Then, a dialog can be added to the application layout to notify the user:

import '@vaadin/confirm-dialog';

// Add the dialog to the rendered html
  header="Logged out"
  <p>You have been logged out. Do you want to log in again?</p>

// Then add the event handlers
private async stayOnPage() {
  await logout(); // Logout on the server
  appStore.clearAuthInfo(); // Logout on the client

private async loginAgain() {
  await logout(); // Logout on the server
  location.href = appStore.authInfo.loginLink!;

You can trigger a logout externally using the provider tools. For Keycloak, you can sign out a session from the admin console or visit the page