# 🔐 `@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. [![npm version](https://img.shields.io/npm/v/@saptools/cf-xsuaa.svg?style=flat&color=CB3837&logo=npm)](https://www.npmjs.com/package/@saptools/cf-xsuaa) [![license](https://img.shields.io/npm/l/@saptools/cf-xsuaa.svg?style=flat&color=blue)](./LICENSE) [![node](https://img.shields.io/node/v/@saptools/cf-xsuaa.svg?style=flat&color=339933&logo=node.js&logoColor=white)](https://nodejs.org) [![install size](https://packagephobia.com/badge?p=@saptools/cf-xsuaa)](https://packagephobia.com/result?p=@saptools/cf-xsuaa) [![types](https://img.shields.io/npm/types/@saptools/cf-xsuaa.svg?style=flat&color=3178C6&logo=typescript&logoColor=white)](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!