0

I use spring boot as backend in addition to thymeleaf as the template engine. Atm, I'm trying to implement oauth2 (with keycloak) into my project.

I created a new realm, added a redirect-uri ("http://172.31.52.123:8000/*") + created users and put the id, secret, etc. in my application.properties file.

When I call http://172.31.52.123:8000/ I get the "hi" message from the ViewController below. When I call http://172.31.52.123:8000/greeting, I will be redirected to http://172.31.52.123:8080/oauth2/authorization/appliance and then to the keycloak login. From there, I get these parameters:

response_type: code
client_id: myClientId
state: hpcfsknjW6QCfMSQWS-k...
redirect_uri: http://172.31.52.123:8080/*

and then these from keycloak again:

state: hpcfsknjW6QCfMSQWS-k...
session_state: f6ca95e5-a117-...
code: 298f32f-f283f ... 

After the login, I end up with this:

172.31.52.123 hat Sie zu oft weitergeleitet. -> ERR_TOO_MANY_REDIRECTS

There is nothing in the console. What am I doing wrong? Originally, it should redirect to http://172.31.52.123:8000/*.

application.properties

appliance-base-url: https://authServerBlaBla/auth/realms/myRealmName

spring:
  security:
    oauth2:
      client:
        registration:
          appliance:
            authorizationGrantType: authorization_code
            redirectUri: http://172.31.52.123:8080/*
            clientId: myClientId
            clientSecret: myClientSecret
        provider:
          appliance:
            authorization-uri: ${appliance-base-url}/protocol/openid-connect/auth
            token-uri: ${appliance-base-url}/protocol/openid-connect/token
            user-info-uri: ${appliance-base-url}/protocol/openid-connect/userinfo

SecurityConfig

@EnableWebSecurity
public class SecurityConfiguration {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeRequests(authorizeRequests -> authorizeRequests
                    .mvcMatchers("/").permitAll()
                    .anyRequest().authenticated()
            )
            .oauth2Login(withDefaults());

        return http.build();
    }
}

ViewController

@Controller
public class ViewController {

    @GetMapping(value = {"/"})
    @ResponseBody
    public String index() {
        return "hi";
    }

    @GetMapping("/greeting")
    @ResponseBody
    public String greet() {
        String username = SecurityContextHolder.getContext().getAuthentication().getName();
        return "Welcome, " + username;
    }
}
Jeff Cook
  • 7,956
  • 36
  • 115
  • 186
letsgetraw
  • 193
  • 1
  • 10
  • https://stackoverflow.com/questions/74571191/use-keycloak-spring-adapter-with-spring-boot-3/74572732?r=SearchResults&s=12%7C0.0000#74572732 – ch4mp Nov 29 '22 at 16:09

2 Answers2

0

You return @ResponseBody => your app should be configured as resource-server (not client).

If your app also serves UI elements (with Thymeleaf, JSF or whatever server-side rendering framework), you'll have to provide two different filter-chains, with securityMatcher patterns to specify where to apply resource-server security and where to apply client one.

I already detailed that in this answer: Use Keycloak Spring Adapter with Spring Boot 3

Details for configuring a Spring backend as both a resource-server (REST API served with @RestController) and a client (server-side rendered HTML with a regular WebMvc @Controller with Thymeleaf, JSF or whatever) in this one of my tutorials.

ch4mp
  • 6,622
  • 6
  • 29
  • 49
  • Hi, thanks for the reply. I'm currently trying your solution and spring complains about the securityMatcher(...) being undefined for the type HttpSecurity. Also requestMatchers(...) "is not applicable for the arguments (String)". Somehow I get a couple of error popping up. For the "jwt -> new JwtAuthenti..." syntax it throws me "this expression must be a functional interface". At this point I do not really know how to continue. Do you have a list of what imports you have there? – letsgetraw Nov 30 '22 at 10:56
  • You are probably not using spring-boot 3 which is the version for which the answer was written. Refer to your Spring version documentation or use your IDE auto-completion – ch4mp Nov 30 '22 at 14:56
  • I figured that when using a server-site template engine (thymeleaf + spring), the project is conisdered a client. Thus, your answer doesn't work for me. – letsgetraw Dec 05 '22 at 13:45
  • My answer contains that: Thymeleaf pages are UI elements). – ch4mp Dec 05 '22 at 15:18
-1

After having a look in the book:

Keycloak - Identity and Access Management for Modern Applications: Harness the Power of Keycloak, OpenID Connect, and OAuth 2.0 Protocols to Secure Applications

I figured that using Spring Boot + Thymeleaf requires keycloak to identify the application as a client. This might be the case in addition to the authorization code grant type.

All I had to do was the following:

  1. change paths into relative paths (requests url)
  2. remove/add proxies for the communication to work
  3. add the following into your pom:

https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-oauth2-client

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-client</artifactId>
    <version>3.0.0</version>
</dependency>

https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-security

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
    <version>3.0.0</version>
</dependency>
  1. create an application.properties/yaml file with your configuration:
appliance-base-url: https://yourKeycloakInstance/auth/realms/p4udemo

spring:
  security:
    oauth2:
      client:
        registration:
          democlient:
            provider: keycloak
            client-id: democlient
            client-secret: 73... 324
            authorization-grant-type: authorization_code
            redirect-uri: "http://yourwebsite:port/login/oauth2/code/"
            scope: openid
            
        provider:
          keycloak:
            authorization-uri: ${appliance-base-url}/protocol/openid-connect/auth
            token-uri: ${appliance-base-url}/protocol/openid-connect/token
            jwk-set-uri: ${appliance-base-url}/protocol/openid-connect/certs
  1. Add securityConfiguration.java (can be renamed differently):
@EnableWebSecurity
public class SecurityConfiguration {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .cors()
            .and()
            .csrf()
            .disable()
            .authorizeRequests(authorizeRequests -> authorizeRequests
                    .anyRequest().authenticated()
            )
            .oauth2Login(withDefaults());
        return http.build();
    }
}
letsgetraw
  • 193
  • 1
  • 10
  • Configuring your app as a client **only** is a bad idea: your API (the @controllers with @ResponseBody) are not secured correctly. The "good" way of proceeding is as I state in my answer and detail in the answer linked: instantiate 2 security filter chains (a resource-server one for API and a client one for UI) – ch4mp Dec 05 '22 at 15:23
  • Can't I also configure spring as a resource server and then forward the bearer token to the javascript requests I send? I tried your config but I don't get it to work. – letsgetraw Dec 08 '22 at 11:30
  • Of course, requests sent from all clients (including Thymeleaf pages) to secured resource-server endpoints (@RestControllers) should be authorized (have a Bearer authorization header with **access** token). With that level of information on your current implementation and errors you have, all I can recommend is waiting until the end of the holiday season, maybe will it get back to work by itself. – ch4mp Dec 08 '22 at 15:22
  • So in addition to your post (including resource + client settings), I need to forward the bearer token to my requests coming from the client? – letsgetraw Dec 12 '22 at 12:21
  • To be sure things are clear, your browser is not an OAuth2 client when fetching Thymeleaf pages. Only Thymeleaf engine on Spring backend is. Requests security between browser and Thymeleaf engine is done using sessions. Maybe [this one of my tutorials](https://github.com/ch4mpy/spring-addons/tree/master/samples/tutorials/resource-server_with_ui) will help you send OAuth2 authorized requests from a Spring `client` (Thymeleaf `@Controller`) to a resource-server (`@RestController`) – ch4mp Dec 13 '22 at 18:22