Keycloack + Spring Security + Vaadin 14 Integration

I’m trying to develop a Spring Boot application where I use keycloack as the authorization server for the OAuth2 authorization code grant type. In my Spring Security configuration, I use the KeycloakWebSecurityConfigurerAdapter adapter from the keycloak-spring-boot-starter dependency. I use Vaadin 14 for my front-end and currently I’m trying to find a way to integrate all 3 of them.

What The Problem Is

By following this [Vaadin tutorial]
(https://vaadin.com/learn/tutorials/securing-your-app-with-spring-security/setting-up-spring-security), I managed to solve lots of Vaadin related security configuration issues. Problems start in the Secure Router Navigation section. Because Vaadin works an an SPA, AJAX requests don’t pass the Spring Security filters (therefore Keycloack’s filters too) and security isn’t working when using the Router API. The tutorial’s suggested solution doesn’t solve my use case as I’m depending on my authorization server to display the login page and I don’t use any Vaadin login view.

My temporary workaround

Only by breaking the SPA nature of the Vaadin app, I succeeded in making everything work:

Button button = new Button("See Customers");
        button.addClickListener(e ->
                button.getUI().ifPresent(ui ->
                        ui.getPage().setLocation("/customers"))
        );

This code forces the users to make a normat HTTP GET request to get the Customers view. This workaround practically makes the Router API almost useless and I foresee that this will cause some serious issues in the future development of the app.

What I’m looking for

Basically, a complete integration where Spring Security can be able to enforce the filters when Vaadin’s RouterLink is called. If that’s totally impossible, then I’m open to hear other suggestions on how can I tackle with this situation.

The typical URL based approach unfortunately doesn’t work with Vaadin. Something like this:

http
  .authorizeRequests()
  .antMatchers("/customers*").hasRole("USER");

does not actually secure the view that you have mapped to @Route("customers") when you use Vaadin’s navigation. Vaadin’s AJAX requests do indeed go through the security filter chain, but not e.g. at the /customers URL. That’s why it doesn’t work as expected.
And that’s why a BeforeEnterListener is used in the tutorial you linked. It’s called before showing each view and allows you to enforce any access rules you have, regardless of how the user navigated there.

So there is a bit of a gap between Vaadin and Spring Security’s HTTP request based security. That’s basically why I created my add-on: [Spring Boot Security for Vaadin Flow]
(https://vaadin.com/directory/component/spring-boot-security-for-vaadin-flow). I think it would get you almost where you want to go. However, there is still one piece missing: Currently I assume a Vaadin view to login. Getting rid of everything non-Vaadin to achieve a smooth experience was a primary goal. But for Keycloak (and web-based SSO solutions in general) this isn’t really enough, of course. But it’s a use case that I’d like to support. So if my add-on looks interesting to you, let me know. The part that handles the “access denied” would have to be a bit more customizable, so it can essentially replicate what the KeycloakAuthenticationEntryPoint does. Basically redirect to Keycloak’s login page.

Patrick Schmidt:
The typical URL based approach unfortunately doesn’t work with Vaadin. Something like this:

http
  .authorizeRequests()
  .antMatchers("/customers*").hasRole("USER");

does not actually secure the view that you have mapped to @Route("customers") when you use Vaadin’s navigation. Vaadin’s AJAX requests do indeed go through the security filter chain, but not e.g. at the /customers URL. That’s why it doesn’t work as expected.
And that’s why a BeforeEnterListener is used in the tutorial you linked. It’s called before showing each view and allows you to enforce any access rules you have, regardless of how the user navigated there.

So there is a bit of a gap between Vaadin and Spring Security’s HTTP request based security. That’s basically why I created my add-on: [Spring Boot Security for Vaadin Flow]
(https://vaadin.com/directory/component/spring-boot-security-for-vaadin-flow). I think it would get you almost where you want to go. However, there is still one piece missing: Currently I assume a Vaadin view to login. Getting rid of everything non-Vaadin to achieve a smooth experience was a primary goal. But for Keycloak (and web-based SSO solutions in general) this isn’t really enough, of course. But it’s a use case that I’d like to support. So if my add-on looks interesting to you, let me know. The part that handles the “access denied” would have to be a bit more customizable, so it can essentially replicate what the KeycloakAuthenticationEntryPoint does. Basically redirect to Keycloak’s login page.

That’s a very nice project ! I will take a look at it tomorrow and see how well it integrates. The project I’m working on is not destined for production, it’s actually a university project, so I’m open to experimentation.

One more question out of curiosity: I told above that a dirty hack was to force the user to make the HTTP request when the user is not authenticated. I tried to force that through the BeforeEnterEvent but I cannot make it work. Any ideas why the below code doesn’t work ?

@Component
public class ConfigureUIServiceInitListener implements VaadinServiceInitListener
{

    @Override
    public void serviceInit(ServiceInitEvent event) {
        event.getSource().addUIInitListener(uiEvent -> {
            final UI ui = uiEvent.getUI();
            ui.addBeforeEnterListener(this::beforeEnter); //
        });
    }

    private void beforeEnter(BeforeEnterEvent event) {
        if (CustomerView.class.equals(event.getNavigationTarget()) //
                && !SecurityUtils.isUserLoggedIn()) { //
            event.getUI().getPage().setLocation("customers"); // Should force the client to make an HTTP GET to /customers
        }
    }
}

I tried it and setLocation(...) does work for me in a BeforeEnterListener. BUT: It will only go to the URL after showing the secured view which is a big fat no-go :slight_smile: But I can reroute to a special view that will then do nothing but the actual redirection.

Patrick Schmidt:
I tried it and setLocation(...) does work for me in a BeforeEnterListener. BUT: It will only go to the URL after showing the secured view which is a big fat no-go :slight_smile: But I can reroute to a special view that will then do nothing but the actual redirection.

That explains a lot, I didn’t even wait for the setLocation() to work since I was seeing the secured view. Will try your approach with the special view. Also, today I’m going to start a new branch and try to integrate your add-on library. I’m going to provide feedback as soon as I can :slight_smile:

I have a working example now that uses Keycloak for authentication, based on my regular demo that you can find on [GitLab]
(https://gitlab.com/codecamp-de/vaadin-security-spring-demo/-/tree/keycloak).
It uses a SNAPSHOT version of the add-on; see the POM for the repository containing it.
As Keycloak setup I created a realm “myrealm”, a client called “login-app” and a user with the role “ADMIN”. I think that’s pretty much it.
So, check it out and let me know what you think or if you have any questions about it.

Patrick Schmidt:
I have a working example now that uses Keycloak for authentication, based on my regular demo that you can find on [GitLab]
(https://gitlab.com/codecamp-de/vaadin-security-spring-demo/-/tree/keycloak).
It uses a SNAPSHOT version of the add-on; see the POM for the repository containing it.
As Keycloak setup I created a realm “myrealm”, a client called “login-app” and a user with the role “ADMIN”. I think that’s pretty much it.
So, check it out and let me know what you think or if you have any questions about it.

A truly amazing work. I just tested your example and works perfectly. I’m going to start integrating your library to my project. I will provide feedback as soon as possible. Also, is there any issue tracker for this project? I find it more suitable to post future issues there instead of this thread.

The project on GitLab has an [issue tracker]
(https://gitlab.com/codecamp-de/vaadin-security-spring/-/issues), but you’d need a GitLab account for that. Otherwise the [discussions section the add-on page]
(https://vaadin.com/directory/component/spring-boot-security-for-vaadin-flow/discussions) should be fine for smaller problems.