@saptools/cf-xsuaa
v0.1.10
Published
Fetch XSUAA credentials and OAuth2 access tokens from SAP BTP Cloud Foundry apps
Maintainers
Readme
🔐 @saptools/cf-xsuaa
Stop copy-pasting XSUAA tokens from the BTP cockpit.
Fetch XSUAA credentials and OAuth2 access tokens from SAP BTP Cloud Foundry apps — straight from your terminal, with intelligent caching built in.
Install • Quick Start • CLI • API • FAQ
✨ Features
- 🔑 Zero-config OAuth2 — fetches
client_credentialstokens straight from the XSUAA binding of any CF app - 💾 Smart caching — reuses tokens until they expire so you do not hand out stale JWTs
- 🧩 CLI & API — drop into shell scripts, Node pipelines, or your favorite test runner
- 🔗 CF-aware — resolves CF API endpoints from region keys via
@saptools/cf-sync, no manual URLs - 🔒 Type-safe — shipped with full TypeScript definitions
- 🪶 Tiny — one dependency (
commander) and zero runtime magic
📦 Install
# Global CLI
npm install -g @saptools/cf-xsuaa
# Or as a dependency
npm install @saptools/cf-xsuaa
# pnpm add @saptools/cf-xsuaa
# yarn add @saptools/cf-xsuaa[!NOTE] Requires Node.js ≥ 20 and the
cfCLI onPATH. For the first secret fetch, setSAP_EMAILandSAP_PASSWORD.
🚀 Quick Start
# 1. Tell cf-xsuaa who you are (only needed for the first secret fetch)
export SAP_EMAIL="[email protected]"
export SAP_PASSWORD="your-sap-password"
# 2. Grab a token (auto-fetches the XSUAA binding on first call, caches it forever)
cf-xsuaa get-token-cached \
--region ap10 --org my-org --space dev --app my-srvThat's it. Copy the printed JWT into curl, Postman, bruno, or wherever you need it. Next call reuses the cached token until it expires.
🧰 CLI
Every command identifies an app with the same four flags:
| Flag | Description | Example |
| --- | --- | --- |
| -r, --region <key> | CF region key | ap10, eu10, us10 |
| -o, --org <name> | CF org name | my-org |
| -s, --space <name> | CF space name | dev |
| -a, --app <name> | CF app name | my-srv |
🔎 cf-xsuaa fetch-secret
Pull the XSUAA client credentials out of the app's VCAP_SERVICES and cache them to disk. Run this once per app, or whenever the binding rotates.
cf-xsuaa fetch-secret --region ap10 --org my-org --space dev --app my-srv🎟️ cf-xsuaa get-token
Fetch a fresh OAuth2 client_credentials token and print the JWT to stdout. Auto-runs fetch-secret first if the binding isn't cached yet.
cf-xsuaa get-token --region ap10 --org my-org --space dev --app my-srv⚡ cf-xsuaa get-token-cached
Return the cached token if it's still valid, otherwise fetch a new one. This is what you want 99% of the time.
TOKEN=$(cf-xsuaa get-token-cached --region ap10 --org my-org --space dev --app my-srv)
curl -H "Authorization: Bearer $TOKEN" https://my-srv.cfapps.ap10.hana.ondemand.com/api/health[!TIP] Cached tokens are refreshed before they become stale, so callers do not receive a JWT that is about to expire.
🧑💻 Programmatic Usage
import {
fetchSecret,
getToken,
getTokenCached,
readStore,
xsuaaDataPath,
} from "@saptools/cf-xsuaa";
const ref = {
region: "ap10",
org: "my-org",
space: "dev",
app: "my-srv",
} as const;
// One-time: cache the client credentials
await fetchSecret(ref);
// Every call: reuse a cached token when possible
const token = await getTokenCached(ref);
// Or: force a fresh token
const freshToken = await getToken(ref);
// Introspect the cache
const store = await readStore();
console.log(`${store.entries.length} apps cached in ${xsuaaDataPath()}`);🧪 Dependency injection (great for tests)
await getToken(ref, {
fetchCredentials: async () => ({
clientId: "cid",
clientSecret: "csec",
url: "https://uaa.example.com",
}),
fetchToken: async () => "fake-jwt",
now: new Date("2026-04-18T00:00:00Z"),
});| Export | Description |
| --- | --- |
| fetchSecret(ref) | Cache a freshly-fetched XSUAA binding |
| getToken(ref) | Force a new OAuth2 token |
| getTokenCached(ref) | Reuse cache, fall through on expiry |
| readStore() / writeStore(store) | Read / write the on-disk store |
| findEntry(store, ref) | Look up a single entry |
| upsertSecret(store, ref, creds) | Merge credentials into a store |
| upsertToken(store, ref, token) | Merge a token into a store |
| fetchClientCredentialsToken(creds) | Low-level UAA call |
| parseXsuaaFromVcap(stdout) | Parse cf env output |
| decodeJwtPayload(jwt) | Decode the JWT payload without verification |
| computeExpiryIso(jwt) / isExpired(iso) | Expiry math |
| xsuaaDataPath() / saptoolsDir() | Resolve on-disk paths |
📁 Output File
All state lives in a single JSON file under your home directory:
~/.saptools/xsuaa-data.json{
"version": 1,
"entries": [
{
"region": "ap10",
"org": "my-org",
"space": "dev",
"app": "my-srv",
"credentials": {
"clientId": "sb-xsappname!t123",
"clientSecret": "<redacted>",
"url": "https://my-org.authentication.ap10.hana.ondemand.com",
"xsappname": "my-app!t123"
},
"token": {
"accessToken": "eyJhbGciOi...",
"expiresAt": "2026-04-18T12:34:56.000Z"
},
"fetchedAt": "2026-04-18T12:20:00.000Z"
}
]
}[!IMPORTANT] Prefer the CLI or exported APIs over parsing this file directly — the on-disk format is an implementation detail.
❓ FAQ
No. Those are only read when cf-xsuaa has to refresh the VCAP-bound client secret. Once the secret is cached, token refreshes go straight to the UAA with client_credentials — no SAP user credentials required.
cf oauth-token returns your personal UAA token. cf-xsuaa returns the app's own service token (issued to the XSUAA clientId in VCAP_SERVICES), which is what you actually need when calling the app's protected endpoints.
No. ~/.saptools/xsuaa-data.json contains clientSecret and live JWTs. It lives under your home directory and should never be checked into git.
Run cf-xsuaa fetch-secret again with the same --region/--org/--space/--app flags and the entry will be overwritten.
🛠️ Development
From the monorepo root:
pnpm install
pnpm --filter @saptools/cf-xsuaa build
pnpm --filter @saptools/cf-xsuaa typecheck
pnpm --filter @saptools/cf-xsuaa test:unit
pnpm --filter @saptools/cf-xsuaa test:e2eThe e2e suite auto-discovers a real CF app with an xsuaa service binding by scoring candidates from ~/.saptools/cf-structure.json. To pin a specific target:
export E2E_TARGET="ap10/my-org/my-space/my-srv"🌐 Related
- 📦
@saptools/cf-sync— sync the CFregion → org → space → apptree to disk - 🗂️ saptools monorepo — the full toolbox
👨💻 Author
dongtran ✨
📄 License
MIT
Made with ❤️ to make your work life easier!
