Technical Articles
Securing your Java application on SAP HANA XSA
The SAP Cloud Application Programming Model (CAP) makes securing your cloud applications on SAP Cloud Platform simple and straightforward.
Thankfully for us, HANA extended application services (XS Advanced) is largely compatible with the SAP Cloud Platform, therefore, making it quite easy to migrate our applications from the on-premise world of HANA XS Advanced to SAP Cloud Platform and vice-versa.
In this blog post, we will have a look at how to secure Java applications deployed to SAP HANA XS Advanced. We also address a minor incompatibility on HANA XS Advanced that might prevent your authenticated users from logging into your Java application.
On both platforms, security revolves around one central component – the XSUAA service. The XSUAA service is an authentication server based on the OAuth 2.0 protocol. It is responsible for generating access tokens that a user can use to access a web service. The web service, in turn, is configured to trust access tokens generated by the XSUAA service – this is done by binding the web service to an XSUAA service instance – as described in the SAP CAP documentation here
Spring Boot
In this blog, we will assume you are building your Java Application using Spring Boot – by default, initiating a Java project with the SAP CDS development kit (available here) generates a Spring Boot application for you.
To enable authentication with Spring Boot, you simply add the following dependencies to the generated pom.xml file of your service:
The XSUAA Spring Boot Starter
<dependency>
<groupId>com.sap.cloud.security.xsuaa</groupId>
<artifactId>xsuaa-spring-boot-starter</artifactId>
<version>${xsuaa.version}</version>
</dependency>
note: the
${xsuaa.version}
should already be defined in theproperties
section of yourpom.xml
(alternatively you can replace this with the current latest version number available at the maven central repository)
and the XSUAA feature
<dependency>
<groupId>com.sap.cds</groupId>
<artifactId>cds-feature-xsuaa</artifactId>
</dependency>
Adding the above-mentioned dependencies will automatically secure all nonpublic endpoints of your service (as defined in your CDS model).
Minor incompatibility with HANA XS Advanced
While the above security configuration will work automatically on the SAP Cloud Platform, there is a minor incompatibility with HANA XSA.
The tokens generated by the XSUAA service on HANA XSA (SPS04 and older) are incompatible with the above-mentioned dependencies. This results in users failing to log in even if they provide the correct login details.
Fortunately, we can work around this by providing a custom Java class to handle the token conversion into something compatible with the above-mentioned dependencies.
In the directory containing your Application.java – the entry point of your Spring Boot application, add the file XsaCompatibleXsuaaConfiguration.java with the contents
package org.yourcompany.YourApplication;
import static com.sap.cloud.security.xsuaa.token.TokenClaims.CLAIM_JKU;
import static com.sap.cloud.security.xsuaa.token.TokenClaims.CLAIM_KID;
import java.net.URI;
import java.net.URISyntaxException;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.web.client.RestOperations;
import com.nimbusds.jwt.JWT;
import com.sap.cloud.security.xsuaa.XsuaaServiceConfiguration;
import com.sap.cloud.security.xsuaa.token.authentication.TokenInfoExtractor;
import com.sap.cloud.security.xsuaa.token.authentication.XsuaaJwtDecoder;
import com.sap.cloud.security.xsuaa.token.authentication.XsuaaJwtDecoderBuilder;
@Configuration
public class XsaCompatibleXsuaaConfiguration {
@Bean
public JwtDecoder xsuaaJwtDecoder(XsuaaServiceConfiguration xsuaaServiceConfiguration,
RestOperations restOperations) {
XsuaaJwtDecoder decoder = (XsuaaJwtDecoder) new XsuaaJwtDecoderBuilder(xsuaaServiceConfiguration)
.withRestOperations(restOperations).build();
decoder.setTokenInfoExtractor(new XsaCompatibleTokenInfoExtractor(xsuaaServiceConfiguration));
return decoder;
}
private static class XsaCompatibleTokenInfoExtractor implements TokenInfoExtractor {
private final XsuaaServiceConfiguration config;
public XsaCompatibleTokenInfoExtractor(XsuaaServiceConfiguration config) {
this.config = config;
}
@Override
public String getJku(JWT jwt) {
String jku = (String) jwt.getHeader().toJSONObject().getOrDefault(CLAIM_JKU, null);
// XSA fallback
if (jku == null) {
jku = config.getUaaUrl() + "/token_keys";
}
return jku;
}
@Override
public String getKid(JWT jwt) {
String kid = (String) jwt.getHeader().toJSONObject().getOrDefault(CLAIM_KID, null);
// XSA fallback
if (kid == null) {
kid = "legacy-token-key";
}
return kid;
}
@Override
public String getUaaDomain(JWT jwt) {
String uaaDomain = config.getUaaDomain();
// XSA fallback
if (uaaDomain == null) {
try {
uaaDomain = new URI(config.getUaaUrl()).getHost();
} catch (URISyntaxException e) {
// bad luck
}
}
return uaaDomain;
}
}
}
This will perform the necessary conversion needed to make your service read and understand the token generated by the older XSUAA service on HANA XSA.
A huge thank you to Marc Becker from the SAP Product & Innovation Tech XS Infrastructure team for the above config code
From here on you can proceed with setting up authorizations for your Java application using the security annotations available in the CDS modeling language as described in the CAP documentation here
In conclusion, we saw that securing your Java application on SAP HANA XS Advanced when using the SAP Cloud Application Programming Model is almost identical to securing the same application on the SAP Cloud Platform the only difference being that you need to cater for the slightly dated XSUAA service on SAP HANA XS Advanced (SPS04), hopefully, this incompatibility is addressed in SAP HANA XS Advanced SPS05.
Phillip Phiri
Co-Innovation Solution Specialist at SAP Co-Innovation Lab