Add XSUAA and AppRouter modules and resources to mta.yaml, understand the provides/requires URL passing pattern, and deploy the full authenticated stack to BTP.
This is where everything comes together. After this topic you have a complete, deployable, authenticated BTP application.
Internet
│
▼
AppRouter (CF app)
│ ① redirects to XSUAA for login
│ ② gets JWT token back
│ ③ forwards request + token to CAP backend
│
▼
CAP Backend (CF app)
│ ④ validates token against XSUAA
│ ⑤ applies @restrict rules
│
▼
SAP HANA (HDI container)
All three arrows — AppRouter → CAP, AppRouter → XSUAA, CAP → XSUAA — are wired via mta.yaml.
_schema-version: "3.1"
ID: bookshop
version: 1.0.0
description: Bookshop — authenticated CAP app on BTP
modules:
# ── 1. CAP Backend ───────────────────────────────────────────
- name: bookshop-srv
type: nodejs
path: gen/srv
parameters:
buildpack: nodejs_buildpack
memory: 256M
disk-quota: 512M
requires:
- name: bookshop-hana # HANA HDI container
- name: bookshop-xsuaa # XSUAA service instance
provides:
- name: srv-api # exports the backend URL
properties:
srv-url: ${default-url} # CF resolves this to the real URL at deploy time
# ── 2. HANA Deployer ─────────────────────────────────────────
- name: bookshop-db-deployer
type: hdb
path: gen/db
requires:
- name: bookshop-hana
# ── 3. AppRouter ─────────────────────────────────────────────
- name: bookshop-app
type: approuter.nodejs
path: app/router # the folder with xs-app.json + package.json
parameters:
memory: 128M
disk-quota: 256M
requires:
- name: bookshop-xsuaa # needs XSUAA for login
- name: srv-api # consumes the URL exported by bookshop-srv
group: destinations # injects it as a named destination
properties:
name: srv-api # destination name used in xs-app.json
url: ~{srv-url} # ~{property} resolves from srv-api.provides
forwardAuthToken: true # appends the user's JWT to proxied requests
resources:
# ── HANA HDI container ───────────────────────────────────────
- name: bookshop-hana
type: org.cloudfoundry.managed-service
parameters:
service: hana
service-plan: hdi-shared
# ── XSUAA service instance ───────────────────────────────────
- name: bookshop-xsuaa
type: org.cloudfoundry.managed-service
parameters:
service: xsuaa
service-plan: application
path: ./xs-security.json # the security descriptor from cds add xsuaa
This is the part that trips up most people:
# bookshop-srv EXPORTS its URL:
provides:
- name: srv-api
properties:
srv-url: ${default-url} # CF magic: resolves to https://bookshop-srv.cfapps.eu10...
# bookshop-app CONSUMES it:
requires:
- name: srv-api
group: destinations
properties:
url: ~{srv-url} # ~{} reads from the srv-api.provides block
${default-url} — a CF MTA placeholder; the deployer substitutes the app's actual URL~{srv-url} — a cross-reference; reads the srv-url property from the srv-api provides blockgroup: destinations — injects all properties under this requires block into the AppRouter's VCAP_APPLICATION.destinations environment variableThis means AppRouter's xs-app.json destination name srv-api resolves automatically — you never hard-code the backend URL.
Without this flag, AppRouter proxies the request without the user's token. Your CAP service receives an unauthenticated request and returns 401 even though the user logged in.
Always set forwardAuthToken: true on any backend destination that uses @requires.
# 1. Compile
cds build --production
# 2. Package
mbt build
# 3. Deploy
cf deploy mta_archives/bookshop_1.0.0.mtar -f
The deployer will:
1. Create the bookshop-hana HDI container (if new)
2. Create the bookshop-xsuaa service instance using xs-security.json (if new)
3. Deploy HANA artifacts via bookshop-db-deployer
4. Start bookshop-srv bound to both HANA and XSUAA
5. Start bookshop-app (AppRouter) with the destination pointing at the live backend URL
After first deployment:
xs-security.json (e.g. "Bookshop Admin")If the user still gets 403, check:
cf logs bookshop-srv --recent | grep -i "scope\|403\|unauthorized"
| Error | Cause | Fix |
|---|---|---|
Error creating service bookshop-xsuaa: invalid scope name |
Scope doesn't start with $XSAPPNAME. |
Fix xs-security.json |
403 on all endpoints after login |
forwardAuthToken: true missing |
Add it to AppRouter destination |
redirect_uri_mismatch |
XSUAA redirect-uris in xs-security.json doesn't match app URL |
Add *.hana.ondemand.com/** wildcard |
AppRouter fails to start: Cannot find module... |
@sap/approuter not in app/router/package.json |
cd app/router && npm install |
JWT token expired during testing |
Default token lifetime is 12 hours | Re-login; for CI use client_credentials grant |