Mastering Custom JWT Creation in SAP Integration Suite (CPI)
Before we dive into the “How,” let’s look at the “What.”
What is a JWT?
A JSON Web Token (JWT) is a compact, URL-safe means of representing claims to be transferred between two parties. It is commonly used for Authentication and Information Exchange.
A JWT looks like a long string of random characters separated by two dots. It has three distinct parts:
Part
Description
Example Content
Header
Specifies the type (JWT) and the hashing algorithm.
{ “alg”: “RS256”, “typ”: “JWT” }
Payload
The “claims” or data about the user.
{ “userId”: “123”, “role”: “admin” }
Signature
A hash created from the header, payload, and a secret key.
(Cryptographic hash)
Crucial Note: The Payload is encoded, not encrypted. Anyone who intercepts the token can read the data inside (like your user ID).
JSON Web Token (JWT) authentication is a stateless way of verifying users. Instead of the server keeping a “session” in its memory or database to remember who you are, it gives you a digital “passport” (the token) that you carry with you.
How It Works
Sender creates a JWT signed with a secret key and send JWT token to receiver token URL.Token API: The server verifies token. If they’re correct, it provides access token to sender.Sender: Send the actual data request along with access tokenReceiver main API: The server verifies access token; if it matches, then accept the data
Pro Tip: You can always debug and visualize your tokens at jwt.io. It is the industry-standard tool for verifying your token structure and signatures.
The Business Scenario: Why do we need this?
Modern integration isn’t just about moving data; it’s about moving data securely.
Imagine you are integrating SAP S/4HANA with a premium cloud service like Salesforce, Google Cloud, banks or a Government Tax Portal. These platforms often require OAuth 2.0 JWT Bearer Flow. Instead of a simple password, your SAP CPI tenant must “prove” its identity by sending a digitally signed JSON Web Token (JWT).
Since SAP CPI doesn’t have a standard “JWT Creator” step, we use a Groovy script to bridge the gap.
SAP CPI Implementation Guide:
While SAP CPI provides many out-of-the-box adapters, there are times when a receiver system requires a custom JWT signed with a private key. Since there is no standard “JWT Creator” step, we use the power of Groovy Scripting.
Step 1: The Keystore Setup
Ensure you have Created a Key Pair to Monitor > Manage Security > Keystore. Note the alias name.
Step 2: The Content Modifier: Set the Pre-requisite
Before the script, use a Content Modifier to set the following properties. This makes the script dynamic and reusable:
Property
Source & Responsibility
Description
alg
Receiver Requirement (API Provider)
The specific algorithm mandated by the target system (e.g., RS256).
typ
Standard Configuration (Created in CPI)
The token format type; always set to JWT.
iss
Receiver Requirement (API Provider)
The unique identifier (Issuer/Client ID) for your SAP system.
sub
Receiver Requirement (API Provider)
The identity or service account email being authenticated.
aud
Receiver Requirement (API Provider)
The specific URL of the target API’s token service.
expiry_time
Flow Logic (Created in CPI)
The business decision on how long a token remains valid.
alias
Security Material (Created in CPI)
The alias of the Key Pair created in the CPI Keystore.
Step 3: The Groovy Script
Technical Breakdown: The 7 Steps to create JWT token
1. Extraction of Metadata (Properties)
The script begins by pulling dynamic values from the Message Properties (set earlier in Content Modifier).
Why? This makes the script reusable across different integration flows by simply changing the properties.
2. Defining the Maps (Data Structuring)
Before we can encode or sign anything, we must organize the raw data into a structured format. We define two Groovy Maps (Key-Value pairs):
The Header Map: Contains the metadata, specifically the algorithm (alg) and the type (typ).The Payload Map: Contains the “Claims.” This includes identity info (iss, sub, aud) and the calculated expiration timestamp (exp).
Why? Defining maps is the cleanest way to prepare data for JSON conversion. It ensures that the exp claim is treated as a Long integer rather than a string, which is a strict requirement for JWT validation.
3. Base64URL Encoding
Standard JSON strings cannot be sent directly in an HTTP header.
The script converts the Maps to JSON and then applies Base64URL encoding.Crucial Step: We use .withoutPadding() because the JWT standard (RFC 7519) forbids the = padding characters used in standard Base64.
4. Construction of the Token (Unsigned)
The encoded Header and encoded Payload are joined using a period (.).
Format: encodedHeader.encodedPayloadThis string serves as the “source of truth” that will be digitally signed in the next step.
5. Digital Signing (The Security Core)
This is the most critical part of the script:
Keystore Access: We use the KeystoreService to fetch the Key Pair based on alias.Algorithm: We initialize Signature using SHA256withRSA (the Java equivalent of RS256).The Seal: The private key “signs” the unsigned token. This ensures that if even one character in the payload is changed, the signature will become invalid.
6. Final Assembly
The generated signature is also Base64URL encoded and appended to the end of the string.
Final Format: Header.Payload.SignatureThis completes the three-part structure of a JWT.
7. Output to Header or Body
Finally, the script writes the resulting token back to the SAP CPI Message:
Header: It creates a header (e.g., jwtToken/Authorization) prefixed with Bearer , making it ready for an HTTP adapter.Body: I have created for testing purpose
Here is the high-performance script to generate the token.
Groovy
import com.sap.gateway.ip.core.customdev.util.Message
import com.sap.it.api.keystore.KeystoreService
import com.sap.it.api.ITApiFactory
import java.security.Signature
import java.util.Base64
import groovy.json.JsonOutput
def Message processData(Message message) {
// — 1. Define variables for 3 different parts
// a. Header variables
def alg = message.getProperty(“alg”)
def typ = message.getProperty(“typ”)
// b. Payload variables
def iss = message.getProperty(“iss”)
def sub = message.getProperty(“sub”)
def aud = message.getProperty(“aud”)
def expiry_time = message.getProperty(“expiry_time”)
long currentTimeInSeconds = new Date().getTime() / 1000L
long exp = currentTimeInSeconds + (expiry_time.toLong() * 60L)
// c. signature varable
def alias = message.getProperty(“Private_KeyPair_alias”)
// — 2. Define Maps
def headerMap = [
“alg”: alg,
“typ”: typ
]
def payloadMap = [
“iss”: iss,
“sub”: sub,
“aud”: aud,
“exp”: exp.toLong() // Ensure numeric value for JWT compliance
]
// — 3. Encode the data —
def encoder = Base64.getUrlEncoder().withoutPadding()
String encodedHeader = encoder.encodeToString(JsonOutput.toJson(headerMap).getBytes(“UTF-8”))
String encodedPayload = encoder.encodeToString(JsonOutput.toJson(payloadMap).getBytes(“UTF-8”))
// — 4. Create unsigned token —
String unsignedToken = encodedHeader + “.” + encodedPayload
// — 5. Signed token —
// a. Fetch Private Key from CPI Keystore
def keystoreService = ITApiFactory.getApi(KeystoreService.class, null)
def keyPair = keystoreService.getKeyPair(alias)
def privateKey = keyPair.getPrivate()
// b. Initialize Signature with RS256 algorithm (SHA256withRSA)
Signature rsa = Signature.getInstance(“SHA256withRSA”)
rsa.initSign(privateKey)
rsa.update(unsignedToken.getBytes(“UTF-8”))
// c. Generate and encode the signature
String encodedSignature = encoder.encodeToString(rsa.sign())
// — 6. Final JWT token —
String jwtToken = unsignedToken + “.” + encodedSignature
// — 7. Save it in header —
message.setHeader(“jwtToken”, “Bearer ” + jwtToken) // You can give Header name as Authorization
message.setBody(jwtToken)
return message
}
Final Output
After our Groovy script generates the JWT, we pass it in the Authorization Header as a “Bearer Token” to the identity provider.
Header Key: Authorization
Header Value: Bearer <Your_Generated_JWT>
The receiver (e.g., Salesforce, Google, bank or Azure) validates the signature of your JWT using the Public Key you shared with them earlier. If valid, they return a JSON response containing a short-lived Access Token.
Finally, you must extract that Access Token and use it in the Authorization header of your actual data request.
Conclusion
Implementing JWT in SAP CPI gives you the flexibility to connect to modern APIs (Google Cloud, Azure, Salesforce, bank’s API etc.) that require custom claims.
Have you implemented JWT differently in your projects? Let’s discuss in the comments!
#SAP #SAPIntegrationSuite #CPI #JWT #Groovy #API #Integration #SAPCloudPlatform
Mastering Custom JWT Creation in SAP Integration Suite (CPI)Before we dive into the “How,” let’s look at the “What.”What is a JWT?A JSON Web Token (JWT) is a compact, URL-safe means of representing claims to be transferred between two parties. It is commonly used for Authentication and Information Exchange.A JWT looks like a long string of random characters separated by two dots. It has three distinct parts:PartDescriptionExample ContentHeaderSpecifies the type (JWT) and the hashing algorithm.{ “alg”: “RS256”, “typ”: “JWT” }PayloadThe “claims” or data about the user.{ “userId”: “123”, “role”: “admin” }SignatureA hash created from the header, payload, and a secret key.(Cryptographic hash)Crucial Note: The Payload is encoded, not encrypted. Anyone who intercepts the token can read the data inside (like your user ID).JSON Web Token (JWT) authentication is a stateless way of verifying users. Instead of the server keeping a “session” in its memory or database to remember who you are, it gives you a digital “passport” (the token) that you carry with you.How It WorksSender creates a JWT signed with a secret key and send JWT token to receiver token URL.Token API: The server verifies token. If they’re correct, it provides access token to sender.Sender: Send the actual data request along with access tokenReceiver main API: The server verifies access token; if it matches, then accept the dataPro Tip: You can always debug and visualize your tokens at jwt.io. It is the industry-standard tool for verifying your token structure and signatures.The Business Scenario: Why do we need this?Modern integration isn’t just about moving data; it’s about moving data securely.Imagine you are integrating SAP S/4HANA with a premium cloud service like Salesforce, Google Cloud, banks or a Government Tax Portal. These platforms often require OAuth 2.0 JWT Bearer Flow. Instead of a simple password, your SAP CPI tenant must “prove” its identity by sending a digitally signed JSON Web Token (JWT).Since SAP CPI doesn’t have a standard “JWT Creator” step, we use a Groovy script to bridge the gap.SAP CPI Implementation Guide:While SAP CPI provides many out-of-the-box adapters, there are times when a receiver system requires a custom JWT signed with a private key. Since there is no standard “JWT Creator” step, we use the power of Groovy Scripting.Step 1: The Keystore SetupEnsure you have Created a Key Pair to Monitor > Manage Security > Keystore. Note the alias name.Step 2: The Content Modifier: Set the Pre-requisiteBefore the script, use a Content Modifier to set the following properties. This makes the script dynamic and reusable:PropertySource & ResponsibilityDescriptionalgReceiver Requirement (API Provider)The specific algorithm mandated by the target system (e.g., RS256).typStandard Configuration (Created in CPI)The token format type; always set to JWT.issReceiver Requirement (API Provider)The unique identifier (Issuer/Client ID) for your SAP system.subReceiver Requirement (API Provider)The identity or service account email being authenticated.audReceiver Requirement (API Provider)The specific URL of the target API’s token service.expiry_timeFlow Logic (Created in CPI)The business decision on how long a token remains valid.aliasSecurity Material (Created in CPI)The alias of the Key Pair created in the CPI Keystore.Step 3: The Groovy ScriptTechnical Breakdown: The 7 Steps to create JWT token1. Extraction of Metadata (Properties)The script begins by pulling dynamic values from the Message Properties (set earlier in Content Modifier).Why? This makes the script reusable across different integration flows by simply changing the properties.2. Defining the Maps (Data Structuring)Before we can encode or sign anything, we must organize the raw data into a structured format. We define two Groovy Maps (Key-Value pairs):The Header Map: Contains the metadata, specifically the algorithm (alg) and the type (typ).The Payload Map: Contains the “Claims.” This includes identity info (iss, sub, aud) and the calculated expiration timestamp (exp).Why? Defining maps is the cleanest way to prepare data for JSON conversion. It ensures that the exp claim is treated as a Long integer rather than a string, which is a strict requirement for JWT validation.3. Base64URL EncodingStandard JSON strings cannot be sent directly in an HTTP header.The script converts the Maps to JSON and then applies Base64URL encoding.Crucial Step: We use .withoutPadding() because the JWT standard (RFC 7519) forbids the = padding characters used in standard Base64.4. Construction of the Token (Unsigned)The encoded Header and encoded Payload are joined using a period (.).Format: encodedHeader.encodedPayloadThis string serves as the “source of truth” that will be digitally signed in the next step.5. Digital Signing (The Security Core)This is the most critical part of the script:Keystore Access: We use the KeystoreService to fetch the Key Pair based on alias.Algorithm: We initialize Signature using SHA256withRSA (the Java equivalent of RS256).The Seal: The private key “signs” the unsigned token. This ensures that if even one character in the payload is changed, the signature will become invalid.6. Final AssemblyThe generated signature is also Base64URL encoded and appended to the end of the string.Final Format: Header.Payload.SignatureThis completes the three-part structure of a JWT.7. Output to Header or BodyFinally, the script writes the resulting token back to the SAP CPI Message:Header: It creates a header (e.g., jwtToken/Authorization) prefixed with Bearer , making it ready for an HTTP adapter.Body: I have created for testing purposeHere is the high-performance script to generate the token.Groovyimport com.sap.gateway.ip.core.customdev.util.Message
import com.sap.it.api.keystore.KeystoreService
import com.sap.it.api.ITApiFactory
import java.security.Signature
import java.util.Base64
import groovy.json.JsonOutput
def Message processData(Message message) {
// — 1. Define variables for 3 different parts
// a. Header variables
def alg = message.getProperty(“alg”)
def typ = message.getProperty(“typ”)
// b. Payload variables
def iss = message.getProperty(“iss”)
def sub = message.getProperty(“sub”)
def aud = message.getProperty(“aud”)
def expiry_time = message.getProperty(“expiry_time”)
long currentTimeInSeconds = new Date().getTime() / 1000L
long exp = currentTimeInSeconds + (expiry_time.toLong() * 60L)
// c. signature varable
def alias = message.getProperty(“Private_KeyPair_alias”)
// — 2. Define Maps
def headerMap = [
“alg”: alg,
“typ”: typ
]
def payloadMap = [
“iss”: iss,
“sub”: sub,
“aud”: aud,
“exp”: exp.toLong() // Ensure numeric value for JWT compliance
]
// — 3. Encode the data —
def encoder = Base64.getUrlEncoder().withoutPadding()
String encodedHeader = encoder.encodeToString(JsonOutput.toJson(headerMap).getBytes(“UTF-8”))
String encodedPayload = encoder.encodeToString(JsonOutput.toJson(payloadMap).getBytes(“UTF-8”))
// — 4. Create unsigned token —
String unsignedToken = encodedHeader + “.” + encodedPayload
// — 5. Signed token —
// a. Fetch Private Key from CPI Keystore
def keystoreService = ITApiFactory.getApi(KeystoreService.class, null)
def keyPair = keystoreService.getKeyPair(alias)
def privateKey = keyPair.getPrivate()
// b. Initialize Signature with RS256 algorithm (SHA256withRSA)
Signature rsa = Signature.getInstance(“SHA256withRSA”)
rsa.initSign(privateKey)
rsa.update(unsignedToken.getBytes(“UTF-8”))
// c. Generate and encode the signature
String encodedSignature = encoder.encodeToString(rsa.sign())
// — 6. Final JWT token —
String jwtToken = unsignedToken + “.” + encodedSignature
// — 7. Save it in header —
message.setHeader(“jwtToken”, “Bearer ” + jwtToken) // You can give Header name as Authorization
message.setBody(jwtToken)
return message
} Final OutputAfter our Groovy script generates the JWT, we pass it in the Authorization Header as a “Bearer Token” to the identity provider.Header Key: AuthorizationHeader Value: Bearer <Your_Generated_JWT>The receiver (e.g., Salesforce, Google, bank or Azure) validates the signature of your JWT using the Public Key you shared with them earlier. If valid, they return a JSON response containing a short-lived Access Token.Finally, you must extract that Access Token and use it in the Authorization header of your actual data request.ConclusionImplementing JWT in SAP CPI gives you the flexibility to connect to modern APIs (Google Cloud, Azure, Salesforce, bank’s API etc.) that require custom claims.Have you implemented JWT differently in your projects? Let’s discuss in the comments!#SAP #SAPIntegrationSuite #CPI #JWT #Groovy #API #Integration #SAPCloudPlatform Read More Technology Blog Posts by Members articles
#SAP
#SAPTechnologyblog