question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Questions/Issues using v2.11.0 with Spring Boot and Identity Service

See original GitHub issue

I am currently testing v2.11.0 of the security libs for multi-tenant validation of Identity Service tokens on CF and K8s.

Good news first, the functionality in general is working as expected! However, I did notice some things.

I was able to use the lib with a WebFilter setup like this:

@WebFilter("/api/*")
public class AuthenticationFilter extends GenericFilterBean {

  private final IasTokenAuthenticator tokenAuthenticator;

  public AuthenticationFilter() {
    tokenAuthenticator = new IasTokenAuthenticator();
  }

  @Override
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
      throws IOException, ServletException {
    try {
       TokenAuthenticationResult authenticationResult = tokenAuthenticator.validateRequest(request, response);
       if (authenticationResult.isAuthenticated()) {
           chain.doFilter(request, response);
       } else {
           // ...
       }
    } finally {
      SecurityContext.clearToken();
    }
  }
}

This is all well and good, but I have another application in which I need the IasTokenAuthenticator to be autowired by Spring:

  private final IasTokenAuthenticator tokenAuthenticator;

  @Autowired
  public AuthenticationFilter(IasTokenAuthenticator tokenAuthenticator) {
    this.tokenAuthenticator = tokenAuthenticator;
  }

This doesn’t work out of the box since the IasTokenAuthenticator is not a bean and hence cannot be autowired. This can be worked around like this:

@Component
public class CustomIasTokenAuthenticator extends IasTokenAuthenticator {

  @Override
  protected OAuth2ServiceConfiguration getServiceConfiguration() {
    return super.getServiceConfiguration();
  }

}

But it would be nicer if this wouldn’t be required.

Second, this setup is inconvenient for running the app locally. Locally, the app defaults to using the CFEnvironment that tries to access environment properties using System::getenv. Spring Boot allows maintaining app configuration in application.yml files. This is easier to maintain in a project than environment variables, since the file can be added to git. Problem is that System::getenv and therefore the CFEnvironment used locally cannot read the properties defined in the application.yml file.

Finally, I was looking through the samples in this repository to see whether there’s also a way to configure everything declaratively using a @Configuration class, instead of wiring it up manually. The only that looked like it is supposed to work with the Identity Service instead of the XSUAA is the spring-security-hybrid-usage sample. However, this did not work for me and the sample itself does not compile when I clone the repo and try to build it.

Building it the way it is, it fails with:

Description:
Field authConverter in sample.spring.security.SecurityConfiguration required a bean of type 'org.springframework.core.convert.converter.Converter' that could not be found.

The injection point has the following annotations:
        - @org.springframework.beans.factory.annotation.Autowired(required=true)

Action:
Consider defining a bean of type 'org.springframework.core.convert.converter.Converter' in your configuration.

I also tried it without the Converter, since I’m currently not interested in the token exchange, but that also failed:

Description:
Method springSecurityFilterChain in org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration required a bean of type 'org.springframework.security.oauth2.jwt.JwtDecoder' that could not be found.

Action:
Consider defining a bean of type 'org.springframework.security.oauth2.jwt.JwtDecoder' in your configuration.

In summary, here are my questions:

  1. What is the recommended way to setup the library for usage with the Identity Service in a Spring Boot application?
  2. Is there a setup where I can autowire the necessary classes out-of-the-box without adding wrapper classes?
  3. Is there a setup where I the lib can read from application.yml when running locally?

Thanks!

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:12 (6 by maintainers)

github_iconTop GitHub Comments

1reaction
mr-flannerycommented, Oct 12, 2021

I found another issue today: When you send a request to the server with an invalid JWT (e.g. bla), it responds with a 500 instead of a 401. See also stacktrace below:

java.lang.IllegalArgumentException: JWT token does not consist of 'header'.'payload'.'signature'.
        at com.sap.cloud.security.xsuaa.jwt.Base64JwtDecoder.decode(Base64JwtDecoder.java:46) ~[token-client-2.11.0.jar:na]
        at com.sap.cloud.security.servlet.HybridTokenFactory.create(HybridTokenFactory.java:49) ~[java-security-2.11.0.jar:na]
        at com.sap.cloud.security.token.Token.create(Token.java:43) ~[java-api-2.11.0.jar:na]
        at com.sap.cloud.security.spring.token.authentication.IasJwtDecoder.decode(IasJwtDecoder.java:45) ~[spring-security-2.11.0.jar:na]
        at org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider.getJwt(JwtAuthenticationProvider.java:97) ~[spring-security-oauth2-resource-server-5.5.2.jar:5.5.2]
        at org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider.authenticate(JwtAuthenticationProvider.java:88) ~[spring-security-oauth2-resource-server-5.5.2.jar:5.5.2]
        at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:182) ~[spring-security-core-5.5.2.jar:5.5.2]
        at org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter.doFilterInternal(BearerTokenAuthenticationFilter.java:130) ~[spring-security-oauth2-resource-server-5.5.2.jar:5.5.2]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.10.jar:5.3.10]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.5.2.jar:5.5.2]
        at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:103) ~[spring-security-web-5.5.2.jar:5.5.2]
        at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:89) ~[spring-security-web-5.5.2.jar:5.5.2]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.5.2.jar:5.5.2]
        at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91) ~[spring-web-5.3.10.jar:5.3.10]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.10.jar:5.3.10]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.5.2.jar:5.5.2]
        at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) ~[spring-security-web-5.5.2.jar:5.5.2]
        at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) ~[spring-security-web-5.5.2.jar:5.5.2]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.10.jar:5.3.10]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.5.2.jar:5.5.2]
        at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:110) ~[spring-security-web-5.5.2.jar:5.5.2]
        at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:80) ~[spring-security-web-5.5.2.jar:5.5.2]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.5.2.jar:5.5.2]
        at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:55) ~[spring-security-web-5.5.2.jar:5.5.2]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.10.jar:5.3.10]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.5.2.jar:5.5.2]
        at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:211) ~[spring-security-web-5.5.2.jar:5.5.2]
        at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:183) ~[spring-security-web-5.5.2.jar:5.5.2]
        at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:358) ~[spring-web-5.3.10.jar:5.3.10]
        at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:271) ~[spring-web-5.3.10.jar:5.3.10]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.45.jar:9.0.45]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.45.jar:9.0.45]
        at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.3.10.jar:5.3.10]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.10.jar:5.3.10]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.45.jar:9.0.45]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.45.jar:9.0.45]
        at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.3.10.jar:5.3.10]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.10.jar:5.3.10]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.45.jar:9.0.45]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.45.jar:9.0.45]
        at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.3.10.jar:5.3.10]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.10.jar:5.3.10]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.45.jar:9.0.45]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.45.jar:9.0.45]
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.45.jar:9.0.45]
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) [tomcat-embed-core-9.0.45.jar:9.0.45]
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542) [tomcat-embed-core-9.0.45.jar:9.0.45]
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143) [tomcat-embed-core-9.0.45.jar:9.0.45]
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [tomcat-embed-core-9.0.45.jar:9.0.45]
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) [tomcat-embed-core-9.0.45.jar:9.0.45]
        at org.apache.catalina.valves.RemoteIpValve.invoke(RemoteIpValve.java:764) [tomcat-embed-core-9.0.45.jar:9.0.45]
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357) [tomcat-embed-core-9.0.45.jar:9.0.45]
        at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374) [tomcat-embed-core-9.0.45.jar:9.0.45]
        at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) [tomcat-embed-core-9.0.45.jar:9.0.45]
        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:893) [tomcat-embed-core-9.0.45.jar:9.0.45]
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1707) [tomcat-embed-core-9.0.45.jar:9.0.45]
        at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.45.jar:9.0.45]
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_292]
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_292]
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.45.jar:9.0.45]
        at java.lang.Thread.run(Thread.java:748) [na:1.8.0_292]

0reactions
mr-flannerycommented, Oct 19, 2021

I found out what the issue was with the AuthenticationEntryPoint not working (and this also gets rid of the 500 when the lib is trying to parse an invalid token).

With the current setup, the filter that is being autowired and that calls into your library is the org.springframework.security.oauth2.server.resource.webBearerTokenAuthenticationFilter. When an exception is being thrown during token validation, it does not delegate to the configured AuthenticationEntryPoint. Instead, it delegates to a AuthenticationFailureHandler. This must be configured separately. The setup isn’t straight-forward, but the following config worked for me:

http
		.cors()
		.and()
		.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
		.and()
		.authorizeRequests()
		.antMatchers("/open-api", "/open-api/**").permitAll()
		.antMatchers("/api", "/api/**").authenticated()
		.anyRequest().denyAll()
		.and()
		.exceptionHandling()
		.authenticationEntryPoint(new CustomAuthenticationEntryPoint())
		.accessDeniedHandler(new CustomAccessDeniedHandler())
		.and()
		.oauth2ResourceServer((oauth2) ->
			oauth2
				.withObjectPostProcessor(new ObjectPostProcessor<BearerTokenAuthenticationFilter>() {
					@Override
					public BearerTokenAuthenticationFilter postProcess(final BearerTokenAuthenticationFilter filter) {
						filter.setAuthenticationFailureHandler(new CustomAuthenticationFailureHandler());
						return filter;
					}
				})
				.jwt()
				.jwtAuthenticationConverter(new CustomTokenAuthenticationConverter())

		);

So summing up, all my problems are solved now. Thanks again for all your help, very much appreciated!

I’d still strongly vote for improving the error message that is logged when the service configuration for the XSUAA/Identity service is missing. I believe that’s something other people will run into as well.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Spring Boot
Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can "just run". We take an opinionated view of...
Read more >
Getting Started | Building an Application with Spring Boot
Navigate to https://start.spring.io. This service pulls in all the dependencies you need for an application and does most of the setup for you....
Read more >
Building web applications with Spring Boot and Kotlin
Using the Initializr Website · Select "Gradle Project" or let the default "Maven Project" depending on which build tool you want to use...
Read more >
Spring Boot 2.6.11 available now
11 has been released and is now available from Maven Central. This release includes 40 bug fixes, documentation improvements, and dependency ...
Read more >
Spring Boot 3.0 Goes GA
This is the first major revision of Spring Boot since 2.0 was released 4.5 years ago. It's also the first GA version of...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found