Getting Started with SSO Kit
SSO Kit builds upon Spring Boot and Spring Security. It comes with a starter and client side modules for configuring the security settings needed to authenticate with an identity provider.
1. Create a Hilla Application
You can create a Hilla application without authentication by entering the following from the command-line:
npx @hilla/cli init --lit <your-project-name>
2. Backend
Once you’ve created a 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 SSO Kit Dependency
Add the sso-kit-starter
module to the pom.xml
file of a Vaadin application like so:
<dependency>
<groupId>dev.hilla</groupId>
<artifactId>sso-kit-starter</artifactId>
<version>2.1.0</version>
</dependency>
Note
|
Version Number
See 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 dependency for your frontend, you have to add a configuration to the Hilla Maven plugin with your application and SSO Kit dependency package names like this:
<build>
<defaultGoal>spring-boot:run</defaultGoal>
<plugins>
<plugin>
<groupId>dev.hilla</groupId>
<artifactId>hilla-maven-plugin</artifactId>
<version>${hilla.version}</version>
<!-- Add this configuration. -->
<configuration>
<parser>
<packages>
<package>com.example.application</package>
<package>dev.hilla.sso.starter</package>
</packages>
</parser>
</configuration>
<!-- ... -->
</plugin>
</plugins>
</build>
2.3. Configure SSO Provider in Spring
Next, you need to set some configuration properties to connect SSO Kit to an OpenID Connect provider. These properties can be added to your application.properties
file where you give the provider URL and the client registration details, such as credentials and scope.
Provider definition is configured within the spring.security.oauth2.provider
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 spring.security.oauth2.registration
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
, and roles
. The following is an example of the properties to set to enable a Keycloak instance to perform authentication:
spring.security.oauth2.client.registration.keycloak.scope=profile,openid,email,roles
# Customize the following property values for your Keycloak configuration:
spring.security.oauth2.client.provider.keycloak.issuer-uri=https://my-keycloak.io/realms/my-realm
spring.security.oauth2.client.registration.keycloak.client-id=my-client
spring.security.oauth2.client.registration.keycloak.client-secret=very-secret-value
2.3.1. Single Sign-On
SSO Kit provides the SingleSignOnConfiguration
auto-configuration class to set up Hilla and Spring to allow single sign-on with external identity providers.
Note
|
Customized Security Configuration
If you need a customized security configuration, you can disable this auto-configuration class by adding its fully-qualified name to the spring.autoconfigure.exclude property and define your own configuration class.
|
The following configuration enables login for the identity providers defined in the application configuration. It instructs the application to accept requests for the login route. It can be configured by setting the hilla.sso.login-route
property, which defaults to /login
.
To redirect users automatically to the provider login form, you can set this property to /oauth2/authorization/{provider-key}
, where {provider-key}
is the key used to configure the provider in application.properties
file.
hilla.sso.login-route=/oauth2/authorization/keycloak
Tip
|
Custom Login Page
Some providers support a custom theme for their login pages. Learn more about this in Theming.
|
2.4. Secure the Application
A Hilla application includes front-end code and backend endpoints. Both of them can and should benefit from authentication protection.
2.4.1. Protect 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 HelloWorldEndpoint.java
with @PermitAll
. When you do this, unauthenticated users won’t be able 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. 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. Install SSO Kit Client Dependency
npm install --save @hilla/sso-kit-client-lit
This dependency contains the SingleSignOnContext
class which is needed in the later steps.
3.2. Add Log-In & Log-Out Buttons
As an example, add two buttons to the drawer footer — one to sign in, and another to sign out. Use the imported ssoContext
to add the login
and the logout
functions to the buttons.
import '@vaadin/button';
import ssoContext from "@hilla/sso-kit-client-lit";
// Replace the `footer` in the rendered `html`.
<footer slot="drawer">
${ssoContext.authenticated
? html`<vaadin-button @click="${ssoContext.logout}">Sign out</vaadin-button>`
: html`<vaadin-button @click="${ssoContext.login}">Sign in</vaadin-button>`
}
</footer>
3.3. Add Access Control
You can protect your views by verifying that each authentication has happened before loading the view.
In the frontend/routes.ts
file, enrich the ViewRoute type with ProtectedRoute
type to be able to protect a view, add the requireAuthentication
parameter to a view, and use the protectRoutes
function to add a protection to the views which requires authentication.
Tip
|
Custom Redirect Path
You can define a custom redirect path in the protectRoutes function on which to redirect users that are not authenticated. The default value is the predefined /ssologin path, which redirects the user to the provider’s login page.
|
import type { ProtectedRoute } from '@hilla/sso-kit-client-lit';
import ssoContext from "@hilla/sso-kit-client-lit";
// Enrich the ViewRoute type with ProtectedRoute.
export type ViewRoute = Route & ProtectedRoute & {
// ...
}
// Add the requireAuthentication parameter to the About view.
{
path: 'about',
// ...
requireAuthentication: true,
},
// Protect the views which require authentication.
export const routes: ViewRoute[] = ssoContext.protectRoutes([
{
path: '',
component: 'main-layout',
children: views,
},
]) as ViewRoute[];
Filter the menu excluding unauthorized views by amending the view filter in main-layout.ts
:
// Gather the hasAccess function and add filter to the views that checks for authentication.
return views
.filter((route) => route.title)
.filter(ssoContext.hasAccess) as RouteInfo[];
Now the About
item in the menu appears only when authenticated.
3.4. Show User Information
The SSO Kit Client provides the User
class which contains information about the authenticated user. You can get the user information by calling the asynchronous SingleSignOnContext.getUser()
function.
Since the About page is now protected, it’s a perfect place to show some information about the current user:
import { property } from 'lit/decorators.js';
import { User } from "@hilla/sso-kit-client-lit";
import ssoContext from "@hilla/sso-kit-client-lit";
// Add a property for the user.
@property()
user: User | undefined;
// Add some output in the AboutView class.
<p>Username: ${this.user?.preferredUsername}</p>
<p>Full name: ${this.user?.fullName}</p>
<p>Email: ${this.user?.email}</p>
// Make the connectedCallback function to be async
// and await the user in the function.
async connectedCallback() {
// ...
this.user = await ssoContext.getUser();
}
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, from the provider’s user dashboard or from another application.
4.2.1. Enable the Feature
To enable the feature in an application, you need to set the hilla.sso.back-channel-logout
property to true
. You would do that like you see here:
hilla.sso.back-channel-logout=true
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
As an example, show a dialog when the user is logged out from outside the application.
The SingleSignOnContext
provided by the SSO Kit Client handles the back-channel logout and receives an event if the logout happens. To get notified about the logout event, register a callback using the onBackChannelLogout
function and store the logged out state:
import ssoContext from "@hilla/sso-kit-client-lit";
// Store the logged out state in the AppStore class.
isLoggedOut = false;
// Subscribe to the back-channel logout event and set logged out state to true on the event.
constructor() {
// ...
ssoContext.onBackChannelLogout(() => {
this.isLoggedOut = true;
});
}
A dialog can be added now to the application layout to notify the user:
import '@vaadin/confirm-dialog';
// Add the dialog to the rendered html.
<vaadin-confirm-dialog
header="Logged out"
cancel-button-visible
@confirm="${ssoContext.login}"
@cancel="${ssoContext.logout}"
.opened="${appStore.isLoggedOut}"
>
<p>You have been logged out. Do you want to log in again?</p>
</vaadin-confirm-dialog>
You can trigger a logout externally with the provider tools. For Keycloak, you can sign out a session from the administration console or visit the page https://my-keycloak.io/realms/my-realm/protocol/openid-connect/logout
.