# ð `@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.
[](https://www.npmjs.com/package/@saptools/cf-xsuaa)
[](./LICENSE)
[](https://nodejs.org)
[](https://packagephobia.com/result?p=@saptools/cf-xsuaa)
[](https://www.typescriptlang.org)
[Install](#-install) âĒ [Quick Start](#-quick-start) âĒ [CLI](#-cli) âĒ [FAQ](#-faq)
---
## âĻ Features
- ð **Zero-config OAuth2** â fetches `client_credentials` tokens 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
```bash
# 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 **`cf` CLI** on `PATH`. For the first secret fetch, set `SAP_EMAIL` and `SAP_PASSWORD`.
---
## ð Quick Start
```bash
# 1. Tell cf-xsuaa who you are (only needed for the first secret fetch)
export SAP_EMAIL="you@company.com"
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-srv
```
That'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 ` | CF region key | `ap10`, `eu10`, `us10` |
| `-o, --org ` | CF org name | `my-org` |
| `-s, --space ` | CF space name | `dev` |
| `-a, --app ` | 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.
```bash
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.
```bash
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.**
```bash
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.
---
## ð Output File
All state lives in a single JSON file under your home directory:
```text
~/.saptools/xsuaa-data.json
```
ðŽ Shape of xsuaa-data.json
```jsonc
{
"version": 1,
"entries": [
{
"region": "ap10",
"org": "my-org",
"space": "dev",
"app": "my-srv",
"credentials": {
"clientId": "sb-xsappname!t123",
"clientSecret": "",
"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
Do I need SAP_EMAIL / SAP_PASSWORD on every call?
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.
How is this different from cf oauth-token?
`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.
Is the cached token safe to commit?
**No.** `~/.saptools/xsuaa-data.json` contains `clientSecret` and live JWTs. It lives under your home directory and should never be checked into git.
How do I invalidate a cached secret?
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:
```bash
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:e2e
```
The 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:
```bash
export E2E_TARGET="ap10/my-org/my-space/my-srv"
```
---
## ð Related
- ðĶ [`@saptools/cf-sync`](https://www.npmjs.com/package/@saptools/cf-sync) â sync the CF `region â org â space â app` tree to disk
- ðïļ [saptools monorepo](https://github.com/dongitran/saptools) â the full toolbox
---
## ðĻâðŧ Author
**dongtran** âĻ
## ð License
MIT
---
Made with âĪïļ to make your work life easier!