0

I have an SAP Cloud SDK project deployed on SAP Cloud Foundry and I want to protect backend APIs with Spring Security without using app router and OAuth solution SAP provides, for example http basic.

The APIs are all working fine with http basic except those invoke APIs of S/4HANA on premise with SAP Cloud SDK.

I have an Authorization header in each http request to access my rest api. For example, Authorization Basic YWRtaW46YWRtaW4=.

I would get the following error message.

com.sap.cloud.sdk.cloudplatform.tenant.exception.TenantAccessException: Failed to get current tenant.
    at com.sap.cloud.sdk.cloudplatform.tenant.AbstractTenantFacade.getCurrentTenantIfAvailable(AbstractTenantFacade.java:143)
    at com.sap.cloud.sdk.cloudplatform.tenant.AbstractTenantFacade.getCurrentTenant(AbstractTenantFacade.java:78)
    at com.sap.cloud.sdk.cloudplatform.tenant.TenantAccessor.getCurrentTenant(TenantAccessor.java:74)
    at com.sap.cloud.sdk.frameworks.hystrix.HystrixUtil.getTenantAndUserIsolatedKey(HystrixUtil.java:104)
    at com.sap.cloud.sdk.frameworks.hystrix.HystrixUtil.getDefaultErpCommandSetter(HystrixUtil.java:153)
    at com.sap.cloud.sdk.frameworks.hystrix.HystrixUtil.getDefaultErpCommandSetter(HystrixUtil.java:135)
    at com.sap.cloud.sdk.s4hana.connectivity.CachingErpCommand.<init>(CachingErpCommand.java:46)
    at com.bosch.smartcustomerportal.commands.GetBillingDocumentByCustomerCommand.<init>(GetBillingDocumentByCustomerCommand.java:50)
    at com.bosch.smartcustomerportal.service.imp.MyBillingDocumentServiceImp.getBillingDocuments(MyBillingDocumentServiceImp.java:78)
    at com.bosch.smartcustomerportal.service.imp.MyBillingDocumentServiceImp$$FastClassBySpringCGLIB$$f72b085b.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:746)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
    at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:88)
    at com.bosch.smartcustomerportal.annotations.MyTimeLogger.log(MyTimeLogger.java:18)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:644)
    at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:633)
    at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:175)
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)
    at com.bosch.smartcustomerportal.service.imp.MyBillingDocumentServiceImp$$EnhancerBySpringCGLIB$$b94c27d2.getBillingDocuments(<generated>)
    at com.bosch.smartcustomerportal.controllers.MyBillingDocumentController.getBillingDocuments(MyBillingDocumentController.java:77)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:215)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:142)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:800)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1038)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:998)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:890)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:634)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:875)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at com.sap.cloud.sdk.cloudplatform.servlet.RequestContextServletFilter.lambda$doFilter$0(RequestContextServletFilter.java:197)
    at com.sap.cloud.sdk.cloudplatform.servlet.RequestContextCallable.call(RequestContextCallable.java:131)
    at com.sap.cloud.sdk.cloudplatform.servlet.RequestContextServletFilter.doFilter(RequestContextServletFilter.java:209)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:320)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:127)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:170)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilterInternal(BasicAuthenticationFilter.java:215)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter.doFilterInternal(DefaultLogoutPageGeneratingFilter.java:52)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter.doFilter(DefaultLoginPageGeneratingFilter.java:206)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:200)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:96)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:74)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:357)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:270)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:791)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1417)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:748)
Caused by: com.sap.cloud.sdk.cloudplatform.tenant.exception.TenantAccessException: com.sap.cloud.sdk.cloudplatform.security.exception.AuthTokenAccessException: Failed to get authorization token.
    at io.vavr.control.Try.getOrElseThrow(Try.java:684)
    at com.sap.cloud.sdk.cloudplatform.tenant.ScpCfTenant.ofCurrentTenant(ScpCfTenant.java:69)
    at com.sap.cloud.sdk.cloudplatform.tenant.ScpCfTenantFacade.resolveCurrentTenant(ScpCfTenantFacade.java:91)
    at com.sap.cloud.sdk.cloudplatform.tenant.TenantRequestContextListener.getProperties(TenantRequestContextListener.java:39)
    at com.sap.cloud.sdk.cloudplatform.servlet.AbstractRequestContextListener.requestContextInitialized(AbstractRequestContextListener.java:40)
    at com.sap.cloud.sdk.cloudplatform.servlet.RequestContextCallable.notifyContextInitialized(RequestContextCallable.java:68)
    at com.sap.cloud.sdk.cloudplatform.servlet.RequestContextCallable.call(RequestContextCallable.java:129)
    ... 78 common frames omitted
Caused by: com.sap.cloud.sdk.cloudplatform.security.exception.AuthTokenAccessException: Failed to get authorization token.
    at com.sap.cloud.sdk.cloudplatform.security.AuthTokenFacade.getCurrentToken(AuthTokenFacade.java:80)
    at com.sap.cloud.sdk.cloudplatform.security.AuthTokenAccessor.getCurrentToken(AuthTokenAccessor.java:71)
    at io.vavr.control.Try.of(Try.java:75)
    ... 84 common frames omitted
Caused by: com.sap.cloud.sdk.cloudplatform.security.exception.AuthTokenAccessException: Failed to decode JWT bearer: no JWT bearer present in 'Authorization' header of request.
    at com.sap.cloud.sdk.cloudplatform.security.AuthTokenDecoder.decode(AuthTokenDecoder.java:188)
    at com.sap.cloud.sdk.cloudplatform.security.AuthTokenRequestContextListener.getProperties(AuthTokenRequestContextListener.java:49)
    ... 81 common frames omitted

2019-09-25 16:42:29.228  WARN 4292 --- [nio-8080-exec-8] .m.m.a.ExceptionHandlerExceptionResolver : Resolved [com.bosch.smartcustomerportal.exception.BusinessException: Failed to get current tenant.]

It seems that the source code is trying to decode the Authorization header as JWT bearer.

Could you give a recommendation how to get rid of this?

By the way, as long as the request includes Authorization header, the sdk would parse it as a known tenant but actually I am not using app router at all.

SAP Cloud SDK version: 2.16.0

Jerry Zhang
  • 179
  • 3
  • 13
  • In version 2 of the SDK we have the strict assumption that you only access the application via an approuter (or some way you receive a valid JWT in the Authorization Header). The easiest way would probably be to upgrade to version 3 of the SDK. To provide a more helpful answer, can you please provide the point at which you receive the mentioned exception? It seems like there the first part is missing there, showing where the Tenant is requested. – Christoph Schubert Sep 25 '19 at 08:11
  • Thanks for your reply. I provided all stack trace for this exception. As for using V3, I am worrying about whether my s/4hana OP version(1805) is compatible with V3. – Jerry Zhang Sep 25 '19 at 08:54
  • Good point, this will probably not be possible out of the box. But for that I would redirect you to this blog post: https://blogs.sap.com/2018/04/30/deep-dive-10-with-sap-s4hana-cloud-sdk-generating-java-vdm-for-s4hana-custom-odata-service/ Therein we describe how you can create your own VDM based on the metadata you get from your OP system. This would allow you to use latest (and greatest) features of the SDK. – Christoph Schubert Sep 25 '19 at 09:42

1 Answers1

1

In the comments you have been given the advice to update to version 3 of SAP Cloud SDK, because there the context properties are resolved in a lenient manner.

In case you want to stay with version 2 of SAP Cloud SDK while still relying on Basic Authentication, then you should consider adding a mocked tenant to your application. Add the following system environment variable:

USE_MOCKED_TENANT=true

Note: This is not a recommended approach, because this will essentially disable the automatic tenant lookup. Therefore a provider/subscriber application setup will be rendered impossible here. Depending on your use case, this may still be an acceptable option.

Alexander Dümont
  • 903
  • 1
  • 5
  • 9