Migrating an XSUAA Authentication Application from SAP Cloud Foundry to SAP Kyma

Introduction

This guide walks through migrating a Node.js application secured with XSUAA from SAP Cloud Foundry to SAP Kyma on SAP BTP.

Cloud Foundry support in SAP BTP trial accounts has become increasingly limited. The ‘cf push’ capability is no longer available, and quotas are restricted, making it difficult to deploy meaningful applications. Given these constraints, I’ve found it more useful to deploy to Kyma instead. Unlike Cloud Foundry’s fully managed approach, Kyma offers Kubernetes-native deployment with direct control over your containers, networking, and scaling. This guide walks through migrating a Cloud Foundry application to Kyma on BTP trial.

Prerequisites

SAP BTP subaccount with Kyma enabledDocker installed (with buildx support for cross-platform builds)kubectl configured for your Kyma clusterAn existing XSUAA service instance on BTP with valid credentialsNode.js application using Express and XSUAA authentication

Step 1: Prepare Your Node.js Application

package.json
{
“name”: “xsuaa-demo”,
“version”: “1.0.0”,
“dependencies”: {
“express”: “^4.18.0”,
“axios”: “^1.6.0”,
“@sap/xsenv”: “^3.4.0”
}
}
server.js
const express = require(‘express’);
const axios = require(‘axios’);
const xsenv = require(‘@sap/xsenv’);
const app = express();
const PORT = process.env.PORT || 3000;
let xsuaaService;

try {
const services = xsenv.getServices({ xsuaa: { tag: ‘xsuaa’ } });
xsuaaService = services.xsuaa;
console.log(‘XSUAA service loaded’);
} catch (err) {
console.error(‘Failed to load XSUAA:’, err.message);
process.exit(1);
}

async function getAccessToken() {
const response = await axios.post(xsuaaService.url, ‘grant_type=client_credentials&response_type=token’, {
auth: { username: xsuaaService.clientid, password: xsuaaService.clientsecret },
headers: { ‘Content-Type’: ‘application/x-www-form-urlencoded’ }
});
return response.data.access_token;
}

app.get(‘/health’, (req, res) => {
res.json({ status: ‘ok’ });
});

app.get(‘/token’, async (req, res) => {
try {
const token = await getAccessToken();
res.json({ access_token: token, token_type: ‘bearer’ });
} catch (err) {
res.status(401).json({ error: ‘Failed to get token’ });
}
});

app.listen(PORT, () => {
console.log(‘Server running on port ‘ + PORT);
});

Step 2: Create Dockerfile

Dockerfile
FROM node:18
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install –production
COPY . .
EXPOSE 3000
CMD [“node”, “server.js”]

Step 3: Build and Push Image

Important: When building on macOS (ARM64), always specify the target platform for amd64.
 
docker buildx build –platform linux/amd64 -t your-registry/xsuaa-demo:latest –push .

Step 4: Deploy to Kyma

 
kubectl create deployment xsuaa-demo –image=your-registry/xsuaa-demo:latest -n default
kubectl expose deployment xsuaa-demo –port=3000 –type=LoadBalancer -n default

Step 5: Configure XSUAA Credentials

 
kubectl create secret generic vcap-services -n default
–from-literal=VCAP_SERVICES='{“xsuaa”:[{“credentials”:{“clientid”:”sb-xsuaa-demo!tXXXXXX”,”clientsecret”:”xxxxxxxx-xxxx…”,”url”:”https://mysubaccount.authentication.us10.hana.ondemand.com/oauth/token”},”label”:”xsuaa”,”tag”:”xsuaa”}]}’

Step 6: Set Environment Variables

 
kubectl set env deployment/xsuaa-demo VCAP_SERVICES='{“xsuaa”:[{“credentials”:{“clientid”:”sb-xsuaa-demo!tXXXXXX”,”clientsecret”:”xxxxxxxx-xxxx…”,”url”:”https://mysubaccount.authentication.us10.hana.ondemand.com/oauth/token”},”label”:”xsuaa”,”tag”:”xsuaa”}]}’ -n default
kubectl rollout restart deployment/xsuaa-demo -n default

Step 7: Verify and Test

 
kubectl logs -f deployment/xsuaa-demo -n default
kubectl get svc xsuaa-demo -n default
EXTERNAL_IP=$(kubectl get svc xsuaa-demo -n default -o jsonpath='{.status.loadBalancer.ingress[0].hostname}’)
curl http://$EXTERNAL_IP:3000/health
curl http://$EXTERNAL_IP:3000/token
Expected Output: You should see “XSUAA service loaded” and “Server running on port 3000” in the logs.

Key Differences: Cloud Foundry vs Kyma

Aspect Cloud Foundry Kyma (Kubernetes) Runtime ManagementFully managed by CFManaged by Kubernetes operatorsService BindingAutomatic via VCAP_SERVICESManual via Kubernetes secrets/env varsDeployment UnitApplication dropletContainer imageScalingCF managedKubernetes HPANetwork PoliciesLimitedFull Kubernetes NetworkPolicy support

Troubleshooting

1. Docker Build Issues on macOS

Error: “exec format error” in pod logs means you built for wrong architecture.

 
docker buildx build –platform linux/amd64 -t your-registry/xsuaa-demo:latest –push .

2. XSUAA Service Not Loading

Check for “Failed to load XSUAA service” in logs:

 
kubectl exec deployment/xsuaa-demo -n default — env | grep VCAP_SERVICES
# Output should show credentials, not placeholder values

3. Token Request Fails with getaddrinfo ENOTFOUND

VCAP_SERVICES still contains placeholder values. Re-apply with actual credentials from BTP.

4. Pod Status Stuck on PodInitializing

If using Istio, disable sidecar injection:

 
kubectl patch deployment xsuaa-demo -n default -p ‘{“spec”:{“template”:{“metadata”:{“annotations”:{“sidecar.istio.io/inject”:”false”}}}}}’
kubectl rollout restart deployment/xsuaa-demo -n default

5. LoadBalancer Not Getting External IP

Use port-forward as alternative:

 
kubectl port-forward svc/xsuaa-demo 3000:3000 -n default

Conclusion

Migrating an XSUAA-secured application from SAP Cloud Foundry to Kyma provides greater flexibility while maintaining strong authentication standards. This approach enables seamless integration with other Kubernetes-based services on SAP BTP while leveraging XSUAA for secure API access.

Next Steps

Implement additional endpoints for your business logicAdd role-based access control (RBAC) using XSUAA scopesConfigure Kubernetes HPA for automatic scalingSet up monitoring and logging with Kyma’s observability stackImplement CI/CD pipelines using SAP Continuous Integration and Delivery

 

​ IntroductionThis guide walks through migrating a Node.js application secured with XSUAA from SAP Cloud Foundry to SAP Kyma on SAP BTP.Cloud Foundry support in SAP BTP trial accounts has become increasingly limited. The ‘cf push’ capability is no longer available, and quotas are restricted, making it difficult to deploy meaningful applications. Given these constraints, I’ve found it more useful to deploy to Kyma instead. Unlike Cloud Foundry’s fully managed approach, Kyma offers Kubernetes-native deployment with direct control over your containers, networking, and scaling. This guide walks through migrating a Cloud Foundry application to Kyma on BTP trial.PrerequisitesSAP BTP subaccount with Kyma enabledDocker installed (with buildx support for cross-platform builds)kubectl configured for your Kyma clusterAn existing XSUAA service instance on BTP with valid credentialsNode.js application using Express and XSUAA authenticationStep 1: Prepare Your Node.js Applicationpackage.json{
“name”: “xsuaa-demo”,
“version”: “1.0.0”,
“dependencies”: {
“express”: “^4.18.0”,
“axios”: “^1.6.0”,
“@sap/xsenv”: “^3.4.0”
}
}server.jsconst express = require(‘express’);
const axios = require(‘axios’);
const xsenv = require(‘@sap/xsenv’);
const app = express();
const PORT = process.env.PORT || 3000;
let xsuaaService;

try {
const services = xsenv.getServices({ xsuaa: { tag: ‘xsuaa’ } });
xsuaaService = services.xsuaa;
console.log(‘XSUAA service loaded’);
} catch (err) {
console.error(‘Failed to load XSUAA:’, err.message);
process.exit(1);
}

async function getAccessToken() {
const response = await axios.post(xsuaaService.url, ‘grant_type=client_credentials&response_type=token’, {
auth: { username: xsuaaService.clientid, password: xsuaaService.clientsecret },
headers: { ‘Content-Type’: ‘application/x-www-form-urlencoded’ }
});
return response.data.access_token;
}

app.get(‘/health’, (req, res) => {
res.json({ status: ‘ok’ });
});

app.get(‘/token’, async (req, res) => {
try {
const token = await getAccessToken();
res.json({ access_token: token, token_type: ‘bearer’ });
} catch (err) {
res.status(401).json({ error: ‘Failed to get token’ });
}
});

app.listen(PORT, () => {
console.log(‘Server running on port ‘ + PORT);
});Step 2: Create DockerfileDockerfileFROM node:18
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install –production
COPY . .
EXPOSE 3000
CMD [“node”, “server.js”]Step 3: Build and Push ImageImportant: When building on macOS (ARM64), always specify the target platform for amd64. docker buildx build –platform linux/amd64 -t your-registry/xsuaa-demo:latest –push .Step 4: Deploy to Kyma kubectl create deployment xsuaa-demo –image=your-registry/xsuaa-demo:latest -n default
kubectl expose deployment xsuaa-demo –port=3000 –type=LoadBalancer -n defaultStep 5: Configure XSUAA Credentials kubectl create secret generic vcap-services -n default
–from-literal=VCAP_SERVICES='{“xsuaa”:[{“credentials”:{“clientid”:”sb-xsuaa-demo!tXXXXXX”,”clientsecret”:”xxxxxxxx-xxxx…”,”url”:”https://mysubaccount.authentication.us10.hana.ondemand.com/oauth/token”},”label”:”xsuaa”,”tag”:”xsuaa”}]}’Step 6: Set Environment Variables kubectl set env deployment/xsuaa-demo VCAP_SERVICES='{“xsuaa”:[{“credentials”:{“clientid”:”sb-xsuaa-demo!tXXXXXX”,”clientsecret”:”xxxxxxxx-xxxx…”,”url”:”https://mysubaccount.authentication.us10.hana.ondemand.com/oauth/token”},”label”:”xsuaa”,”tag”:”xsuaa”}]}’ -n default
kubectl rollout restart deployment/xsuaa-demo -n defaultStep 7: Verify and Test kubectl logs -f deployment/xsuaa-demo -n default
kubectl get svc xsuaa-demo -n default
EXTERNAL_IP=$(kubectl get svc xsuaa-demo -n default -o jsonpath='{.status.loadBalancer.ingress[0].hostname}’)
curl http://$EXTERNAL_IP:3000/health
curl http://$EXTERNAL_IP:3000/tokenExpected Output: You should see “XSUAA service loaded” and “Server running on port 3000” in the logs.Key Differences: Cloud Foundry vs KymaAspect Cloud Foundry Kyma (Kubernetes) Runtime ManagementFully managed by CFManaged by Kubernetes operatorsService BindingAutomatic via VCAP_SERVICESManual via Kubernetes secrets/env varsDeployment UnitApplication dropletContainer imageScalingCF managedKubernetes HPANetwork PoliciesLimitedFull Kubernetes NetworkPolicy supportTroubleshooting1. Docker Build Issues on macOSError: “exec format error” in pod logs means you built for wrong architecture. docker buildx build –platform linux/amd64 -t your-registry/xsuaa-demo:latest –push .2. XSUAA Service Not LoadingCheck for “Failed to load XSUAA service” in logs: kubectl exec deployment/xsuaa-demo -n default — env | grep VCAP_SERVICES
# Output should show credentials, not placeholder values3. Token Request Fails with getaddrinfo ENOTFOUNDVCAP_SERVICES still contains placeholder values. Re-apply with actual credentials from BTP.4. Pod Status Stuck on PodInitializingIf using Istio, disable sidecar injection: kubectl patch deployment xsuaa-demo -n default -p ‘{“spec”:{“template”:{“metadata”:{“annotations”:{“sidecar.istio.io/inject”:”false”}}}}}’
kubectl rollout restart deployment/xsuaa-demo -n default5. LoadBalancer Not Getting External IPUse port-forward as alternative: kubectl port-forward svc/xsuaa-demo 3000:3000 -n defaultConclusionMigrating an XSUAA-secured application from SAP Cloud Foundry to Kyma provides greater flexibility while maintaining strong authentication standards. This approach enables seamless integration with other Kubernetes-based services on SAP BTP while leveraging XSUAA for secure API access.Next StepsImplement additional endpoints for your business logicAdd role-based access control (RBAC) using XSUAA scopesConfigure Kubernetes HPA for automatic scalingSet up monitoring and logging with Kyma’s observability stackImplement CI/CD pipelines using SAP Continuous Integration and Delivery   Read More Technology Blog Posts by Members articles 

#SAP

#SAPTechnologyblog

You May Also Like

More From Author