Technology Blog Posts by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
adarshrao_rao
Product and Topic Expert
Product and Topic Expert
1,092

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:

  • Header: Contains metadata about the token, such as its type and the algorithm used.
  • Payload: This section holds the claims, providing information like user identity and other relevant data.
  • Signature: Validates the integrity and authenticity of the token's contents.

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.

Picture1.pngPicture2.png

 

iFlow Design - Component Breakdown:

Following are the iFlows involved-

  1. Specific iFlow - To receive and post data
  2. Generic iFlow - To get Access Token

Specific iFlow - To receive and post data:

Picture3.png

This iFlow consists of the following:

  • Integration Process:
    • Sender Channel: To receive the request from the sender system
    • Process Call 1: To Call Get Access Token Flow
    • Process Call 2: To call Receiver Flow

Under Runtime Configuration, Allow access_token under Allowed Header(s)

Picture4.png

  • Get Access Token (Local Integration Process):
    • Content Modifier: To set the scope which specific to API which is being called from receiver system
    • Request Reply: To call Generic Access Token Flow
  • Receiver Flow (Local Integration Process):
    • Content Modifier: To set the received access token from the generic flow as Authorization bearer while authenticating the receiver APIpicture5.png
    • Request - Reply: To call Receiver System API

Generic iFlow – To get Access Token:

picture6.png

  • Sender Channel: Configure Process Direct to receive the request from the specific iFlow.
  • Content Modifier: To set headers and exchange properties which are required for JWT generation in the next step

picture7.pngpicture8.png

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.

  • Groovy Script: To generate JWT
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

  1. Property Handling: Gathers IDs and aliases for token generation.
  2. Keystore Integration: Retrieves RSA keys and certificates for signing the JWT.
  3. JWT Formation: Sets up token headers, claims, and signs with RSA.
  4. Token Storage: Holds onto the JWT for later use.
  • Content Modifier: To set HTTP Request Body with the grant_type, assertion and scope (from the specific flow)picture9.png
  • Request – Reply: Call Receiver system and get the access token

For enhanced security, let’s dive into critical considerations:

RSA Implementation

  1. Private Key Handling: Ensure secure storage of private keys, while public keys can be distributed for verification.
  2. Increased Security: RS256 enhances protection with double-key mechanisms suited for distributed environments.
  3. Regulatory Compliance: RSA often satisfies stringent compliance needs due to its robust security architecture.

 Testing:

Whenever the sender system sends data to be posted in receiver system

  1. Specific iFlow will receive the request and calls the Generic iFlow to get Access Token

picture10.png2. Generic iFlow will generate JWT, connects to Receiver system and gets the Access Token which is shared back to the Specific iFlow

picture11.png3. 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!