
In this blog, you'll discover how to set up an SAP Cloud Integration (CI) iFlow that generates OAuth 2.0 access tokens using JWT, focusing on robust security implementations.
If you're interested in less complex security setups—such as symmetric algorithms, shared client secrets, and manual encoding of headers and claims to base64—you might want to check out SAP Cloud Integration: Bloomberg API Integration Using JWT–OAuth. It offers alternative approaches using symmetric encryption.
If you're aiming for higher security, including the use of RSA algorithms involving a private key for signing and a public key for verification, as well as JWT construction utilizing the io.jsonwebtoken library, please continue reading.
JWT Overview:
JSON Web Tokens are essentially compact, URL-safe tokens that carry claims between two parties. Each JWT is composed of three parts:
JWTs are favored for being stateless and secure, streamlining the authentication process efficiently.
Introducing JWTs in SAP Cloud Integration (CI):
Implementing JWTs within SAP Cloud Integration (CI) can elevate your authentication strategy, offering robust methods for session management and API interactions. Since there's no standard way to generate JWTs in SAP Cloud Integration (CI), using Groovy scripts becomes essential for creating them within integration flows. This blog focuses on a secure iFlow design tailored for generating these tokens with advanced security measures.
High Level Design:
Flows can be designed/orchestrated based on the integration requirement. In my case, based on the requirement, I have orchestrated the flows as depicted below.
iFlow Design - Component Breakdown:
Following are the iFlows involved-
Specific iFlow - To receive and post data:
This iFlow consists of the following:
Under Runtime Configuration, Allow access_token under Allowed Header(s)
Generic iFlow – To get Access Token:
I have maintained client_id, token_url and subject in the iFlow which can be externalized. private_key and certificate (public) is stored in the keystore and its alias name is maintained here.
import com.sap.gateway.ip.core.customdev.util.Message
import com.sap.it.api.ITApiFactory
import com.sap.it.api.keystore.KeystoreService
import io.jsonwebtoken.Jwts
import io.jsonwebtoken.SignatureAlgorithm
import java.math.BigDecimal
import java.security.KeyFactory
import java.security.PrivateKey
import java.security.cert.CertificateFactory
import java.security.cert.X509Certificate
import java.security.spec.PKCS8EncodedKeySpec
import java.util.Base64
import java.util.Map
def Message processData(Message message) {
Map<String, Object> map = message.getProperties()
def clientId = map.get("client_id")
def tokenUrl = map.get("token_url")
def privateKeyAlias = map.get("private_key")
def certificateAlias = map.get("certificate")
def subject = map.get("subject") ?: "CN=xxxxxxx, O=Demo, L=xxxx, S=xxxx, C=xx"
KeystoreService keystoreService = ITApiFactory.getService(KeystoreService.class, null)
if (keystoreService == null) {
throw new IllegalStateException("Failed to access KeystoreService")
}
PrivateKey privateKey = keystoreService.getKey(privateKeyAlias)
X509Certificate certificate = keystoreService.getCertificate(certificateAlias)
byte[] privateKeyBytes = privateKey.getEncoded()
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes)
KeyFactory keyFactory = KeyFactory.getInstance("RSA")
PrivateKey rsaPrivateKey = keyFactory.generatePrivate(keySpec)
def certificateString = Base64.getEncoder().encodeToString(certificate.getEncoded())
List<String> x5c = [certificateString]
def jti = UUID.randomUUID().toString().replaceAll("-", "")[0..21]
def iat = new BigDecimal(System.currentTimeMillis() / 1000).setScale(0, BigDecimal.ROUND_DOWN)
def exp = new BigDecimal(System.currentTimeMillis() / 1000 + 900).setScale(0, BigDecimal.ROUND_DOWN)
Map<String, Object> claims = [
iss: clientId,
aud: tokenUrl,
sub: subject,
jti: jti,
exp: exp,
iat: iat,
nbf: iat,
]
String jwtToken = Jwts.builder()
.setHeaderParam("typ", "JWT")
.setHeaderParam("alg", "RS256")
.setHeaderParam("x5c", x5c)
.setClaims(claims)
.signWith(SignatureAlgorithm.RS256, rsaPrivateKey)
.compact()
message.setProperty("jwt", jwtToken)
return message
}
Following are done in this script
For enhanced security, let’s dive into critical considerations:
RSA Implementation
Testing:
Whenever the sender system sends data to be posted in receiver system
2. Generic iFlow will generate JWT, connects to Receiver system and gets the Access Token which is shared back to the Specific iFlow
3. With this Access Token specific flow will be able to post the data to Receiver System API
To sum it up, while SAP Cloud Integration doesn't natively support JWT generation, the use of Groovy scripts fills this crucial gap, allowing your integrations to utilize secure tokens effectively. Whether you're after a straightforward implementation or seeking the higher security standards provided by RSA, this approach gives you flexibility and control over your authentication processes. By adopting these strategies, you are not just enhancing security but you are setting up your API interactions in a way that's prepared to meet today's demanding standards while paving the way for scalable, reliable integrations.
Additional Resources
While these integration flows are robust enough to generate JWTs and obtain access tokens from APIs, there's always room for improvement and optimization. My goal in this blog was to provide you with a foundational understanding of how JWTs can be crafted and utilized within SAP Cloud Integration. I hope this gives you a solid starting point for integrating secure authentication into your projects. If you have any feedback or questions, I'd love to hear from you. Please feel free to share your thoughts in the comment section below!
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
17 | |
17 | |
12 | |
11 | |
9 | |
9 | |
7 | |
6 | |
6 | |
5 |