Angular’s keycloak adapter generates access tokens, that are invalid for REST communication with Spring Boot backend application secured by ta same keycloak’s realm. Localhost addresses were replaced with example.com because of SPAM filters reasons.
Keycloak setup
Testing keycloak 22.0.3 instance on localhost run from bin/kc.sh script on default port Angular and Spring Boot application have different clients, but same realm.
Angular client
Rest of settings are default.
Spring Boot
Rest of settings are default.
Angular adapter
Library used in project: keycloak-angular 14.1.0
Setup in project:
Provider
{
provide: APP_INITIALIZER,
useFactory: initializeKeycloak,
multi: true,
deps: [KeycloakService]
}
Factory
function initializeKeycloak (keycloak: KeycloakService) {
return () =>
keycloak.init ({
config: {
url: 'example.com:8080',
realm: 'SpringBootKeycloak',
clientId: 'angular-client'
},
initOptions: {
onLoad: 'check-sso',
silentCheckSsoRedirectUri:
window.location.origin + '/assets/silent-check-sso.html'
}
});
}
Keycloak service is used for logging and logout. Web authorization in browser works on Angular application. Generating valid tokens for backend doesn't.
Spring Boot Security
application.properties
spring.security.oauth2.client.registration.keycloak.client-id=spring-client
spring.security.oauth2.client.registration.keycloak.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.keycloak.scope=openid
spring.security.oauth2.client.provider.keycloak.issuer-uri=example.com:8080/realms/SpringBootKeycloak
spring.security.oauth2.client.provider.keycloak.user-name-attribute=preferred_username
spring.security.oauth2.resourceserver.jwt.issuer-uri=example.com:8080/realms/SpringBootKeycloak
spring.security.oauth2.resourceserver.jwt.jwk-set-uri=example.com:8880/auth/realms/SpringBootKeycloak/protocol/openid-connect/certs
Security config
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
class SecurityConfig {
SecurityConfig() {
}
@Bean
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
@Order(1)
@Bean
public SecurityFilterChain resourceServerFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(auth -> auth.anyRequest().authenticated());
http.oauth2ResourceServer((oauth2) -> oauth2
.jwt(Customizer.withDefaults()));
return http.build();
}
@Bean
public AuthenticationManager authenticationManager(HttpSecurity http) throws Exception {
return http.getSharedObject(AuthenticationManagerBuilder.class)
.build();
}
}
Sample http requests
request.http This requests give valid token to Spring Boot Application.
Obtaining token
POST example.com:8080/realms/SpringBootKeycloak/protocol/openid-connect/token
Content-Type: application/x-www-form-urlencoded
client_id=spring-client&username=user&password=password&grant_type=password
> {%
client.global.set("token", response.body.access_token);
%}
Using the token in get request.
GET example.com:8081/api/resource
Authorization: Bearer {{token}}
Errors
Reponse in browser with token from adapter
401 Unauthorized
x-frame-options DENY
Stack trace in Spring Boot console
java.lang.StackOverflowError: null
at org.springframework.aop.support.AopUtils.isEqualsMethod(AopUtils.java:151) ~[spring-aop-6.0.9.jar:6.0.9]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:175) ~[spring-aop-6.0.9.jar:6.0.9]
at jdk.proxy2/jdk.proxy2.$Proxy175.authenticate(Unknown Source) ~[na:na]
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:201) ~[spring-security-core-6.1.0.jar:6.1.0]
Solutions that didn't work
I stumbled upon this answer and tried using this solution:
"I had the same problem with a Java Spring Backend, after a lot of google searches, I found out that there were incompatibilities between Signature Algorithm used by spring boot and keycloak.
As part of the initiation I specified SignatureAlgorithm.RS256 in the application and also ensured that same algorithm was used for the realm/client.
I really dont know the specifics on how to use keycloak with nodejs, but for Java Spring I created a @Bean in the WebSecurity class as follows"
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class WebSecurityConfig {
@Value(“${spring.security.oauth2.resourceserver.jwt.jwk-set-uri}”)
private String jwkSetUri;
@Bean
public JwtDecoder jwtDecoder() {
return NimbusJwtDecoder.withJwkSetUri(jwkSetUri).jwsAlgorithm(SignatureAlgorithm.RS256).build();
}
}
But it doesn't seem to help.
I expected to authenticate using token from angular adapter in REST request.
Two things went wrong here: