Technology Blog Posts by SAP
cancel
Showing results for 
Search instead for 
Did you mean: 
Egan
Product and Topic Expert
Product and Topic Expert
1,508

Connecting to a Public Cloud API (from Business Accelerator Hub) via Node.js CAP application can be challenging. I hope this blog helps others attempting the same task.

 Prerequisites

• SAP BTP subaccount (Cloud Foundry)
• Service key for the target Public Cloud API
• XSUAA instance bound to your CAP app
• SAP BTP Destination service instance
• Bruno installed for manual testing

 

1. Confirm the API's Auth Options

First, check which authentication methods are available for the API you plan to consume.

In my example I connect to the Specification Management API, but the steps apply to any Public Cloud API.

If the OAuth 2.0 Access Code Flow is listed under “Authentication Methods”, the API supports the authorization-code flow. That flow confirms a user context and usually grants wider authorisations, such as update and POST requests.

(OAuth 2.0 Application Flow corresponds to the client-credentials flow.)

Egan_0-1753113819867.png

2. Connect manually with Bruno (Good blog on Bruno)

Because our API supports the auth-code flow, we first prove we can log in by calling it via Bruno.

Create a new collection and paste the base URI of your Public Cloud API instance (you’ll find it in the service key in your service instance of the saas application your connecting to).

Egan_3-1753114108426.png

When you send a test GET request you will probably receive an authorisation error. Configure OAuth 2.0 on the Auth tab

Select Grant type Authorisation Code

Then fill in the below fields using your service key from your service instance

Egan_1-1753113819868.png

  • Callback URL: <your Public Cloud API hostname>
  • Auth URL:** https://<subdomain>.authentication.<region>.hana.ondemand.com/oauth/authorize
  • Access-Token URL: https://<subdomain>.authentication.<region>.hana.ondemand.com/oauth/token
  • Client ID / Secret: from the uaa section of the service key

Request the token, pick your identity provider, and Bruno will return an access token. Inspect the scopes there to see what you can do.

3. Connect to the API from Node.js

After the manual check we can now automate the flow in a Node.js service.

3.1 Destination setup

Create a destination that points to the same API endpoint and bind it to your app.

Egan_2-1753113819869.png

The required scopes and the audience (aud) value are visible inside the Bruno access token.

the Audience should start with the client id of your CAP application on BTP

Once the destination is saved, ensure your application is bound to the Destination service.

3.2 xs-security.json

Add every scope your service needs so the approuter, it will forward it in the user’s JWT: in my example i needed this scope.

{
  "name": "$XSAPPNAME.Spc_Write",
  "description": "Allow modification of specifications."
}

Also reference the scope in authorities (and role templates, if used) and assign the resulting role collection to your user.

3.3 Connect in code

Now time to connect to the API in the code itself.

First, obtain the user’s JWT. This helper below tries every possible source and falls back to LOCAL_USER_JWT in local development:

/** Try every source for a JWT and fall back to LOCAL_USER_JWT. */
function getJwt(capReq) {
  // CAP ≥ 7.3 keeps the raw token here
  if (capReq.user?.jwt) return capReq.user.jwt;

  // Plain HTTP / WebSocket headers
  const hdrs = capReq.http?.req?.headers || {};
  const token =
        hdrs['x-approuter-authorization'] ||   // CF WebSocket
        hdrs.authorization;                    // CF HTTP

  if (token) return token.replace(/^Bearer\s+/, '');
  // Last resort for local dev
  return process.env.LOCAL_USER_JWT;
}

3.3.1 Testing locally

When testing locally we don't have access to the user JWT so we can add it manually to the environment file so we can still test locally, below is the process to get the JWT for user testing (each token lasts about 7 to 8 hours)

Step 1 – Get an auth code
https://<instance>.eu10.authentication.eu10.hana.ondemand.com/oauth/authorize
?response_type=code
&client_id=<XSUAA client id of your deployed app>
&redirect_uri=http://localhost
&state=xyz

Paste your version into your browsers address bar, after login you will land on http://localhost/?code=<AUTH_CODE>&state=xyz. Copy the value of AUTH_CODE.

Step 2 – Exchange the code for a JWT

I made the Curl Request inside BAS itself. it will return a number of tokens, we only need the first and biggest one.

curl -v -X POST \
-u '<same client id>' \
--data-urlencode grant_type=authorization_code \
--data-urlencode code=<AUTH_CODE> \
--data-urlencode redirect_uri=http://localhost \
https://<instance>.eu10.authentication.eu10.hana.ondemand.com/oauth/token

Save the token from the response into .env:

LOCAL_USER_JWT=<paste JWT here>

3.3.2 Call the destination with the JWT

const jwt = getJwt(req);
if (!jwt) req.reject(401, 'No user token found.');

const dest = await getDestination({
  destinationName: '<DestinationName>',
  jwt
});
await executeHttpRequest(dest, {
  method: 'POST',
  url: `/odata/v4/api/specification/v1/${EntityName}`,
  data: v
});

If you reach the destination but receive 401 Unauthorized, your JWT is missing the correct scope—ask the product team which one you need.

Other common pitfall is using the different XSUAA instances to get your JWT for the local run. use your applications XSUAA client since this is the same one your application will check the token against

And that’s it! You should now be able to read from and write to the Public Cloud API. For read-only workloads the client-credentials flow is usually simpler.

If I missed anything or you’d like more details, please comment below—happy to help.

 

## Frequently Asked Questions

### What’s the difference between authorization-code and client-credentials flows?
The authorization-code flow runs in a user context, so it can carry user-level scopes (read, write, update). The client-credentials flow operates with a technical user—perfect for background jobs or read-only integrations.

### Why do I get “401 Unauthorized” after deployment?
Your user’s JWT is probably missing the required scope (e.g., `Spc_Write`). Make sure the scope is listed in xs-security.json, mapped to a role template, and assigned via a role collection in the BTP cockpit.

### Which audience value belongs in my destination?
Use the client ID of your CAP application’s XSUAA instance; it usually starts with `sb-<appName>!t<number>`.

Node.jsSAP Cloud Identity ServicesAPISAP AuthenticatorSAP Fiori CloudSAP Cloud Application Programming ModelSAP Business Application StudioSAP Business Technology PlatformSAP S/4HANA Cloud Public Edition ExtensibilitySAP Business Accelerator HubJavaScriptAPI Management