diff --git a/guides/security/remote-authentication.md b/guides/security/remote-authentication.md index 1f3141b0a..52a05d7b4 100644 --- a/guides/security/remote-authentication.md +++ b/guides/security/remote-authentication.md @@ -39,7 +39,7 @@ At the connectivity layer, the following basic tasks can be addressed genericall - Destination (_how to find the target service_) - User propagation (_how to transport user information_) -CAP's connectivity component handles authentication (IAS, XSUAA, X.509, ZTID, ...), destination (local destination, BTP Destination, BTP Service Binding), and user propagation (technical provider, technical subscriber, named user) transparently through configuration. +CAP's connectivity component handles authentication (IAS, XSUAA, X.509, ZTID, ...), destination (local destination, BTP Destination, BTP Service Binding), and user propagation (technical provider, technical subscriber, named user) transparently through configuration (although the configuration approach may differ between Java and Node.js). All three service scenarios can be addressed through configuration variants of the same remote service concept, as shown in the following sections. CAP supports out-of-the-box consumption of various types of [remote services]( #remote-services): @@ -59,10 +59,14 @@ Technically, **they share the same identity instance, which allows direct token ![Co-located services](./assets/co-located-services.drawio.svg){width="450px" } [Learn more about how to configure co-located services in CAP Java](/java/cqn-services/remote-services#binding-to-a-service-with-shared-identity) {.learn-more} +[Learn more about how to configure remote services in CAP Node.js](/node.js/remote-services) {.learn-more} -You can test CAP's built-in support for co-located services in practice by modifying the [`xflights-java`](https://github.com/capire/xflights-java/tree/main) and [`xtravels-java`](https://github.com/capire/xtravels-java/tree/main) sample applications. -`xflights-java` acts as a master data provider exposing basic flight data in service [`sap.capire.flights.data`](https://github.com/capire/xflights-java/blob/6fc7c665c63bb6d73e28c11b391b1ba965b8772c/srv/data-service.cds#L24) via different protocols. -On the client side, `xtravels-java` imports this service as a CAP remote service and fetches data in a [custom handler for data federation](https://github.com/capire/xtravels-java/blob/53a5fa33caf4c9068f2e66fab25bda26f3f450ca/srv/src/main/java/sap/capire/xtravels/handler/FederationHandler.java#L63). +You can test CAP's built-in support for co-located services in practice by modifying the sample applications: +- **Java**: [`xflights-java`](https://github.com/capire/xflights-java/tree/main) and [`xtravels-java`](https://github.com/capire/xtravels-java/tree/main) +- **Node.js**: [`xflights`](https://github.com/capire/xflights/tree/main) and [`xtravels`](https://github.com/capire/xtravels/tree/main) + +`xflights` acts as a master data provider exposing basic flight data in service `sap.capire.flights.data` via different protocols. +On the client side, `xtravels` imports this service as a CAP remote service and fetches data for federation. ::: tip CAP offers a simplified co-located service setup by leveraging remote services that require: @@ -88,7 +92,7 @@ As client, `xtravels-srv` first needs a valid configuration for the remote servi ::: code-group -```yaml [/srv/src/main/resources/application.yaml] +```yaml [Java: application.yaml] --- spring: config.activate.on-profile: cloud @@ -105,26 +109,64 @@ cds: options: url: https:// ``` + +```json [Node.js: package.json] +{ + "cds": { + "requires": { + "sap.capire.flights.data": { + "kind": "hcql", + "[production]": { + "credentials": { + "url": "https:///hcql/data", + "forwardAuthToken": true + } + } + } + } + } +} +``` ::: +::: details Java configuration explained + The `type` property activates the protocol for exchanging business data and must be offered by the provider [CDS service](https://github.com/capire/xflights-java/blob/6fc7c665c63bb6d73e28c11b391b1ba965b8772c/srv/data-service.cds#L24). The `model` property needs to match the fully qualified name of the CDS service from the imported model. You can find CDS service definition of `sap.capire.flights.data` in file `target/cds/capire/xflight-data/service.cds` resolved during CDS build step. The `binding.name` needs to point to the shared identity instance and `options.url` together with `http.suffix` provides the required location of the remote service endpoint. Finally, `onBehalfOf: systemUser` specifies that the remote call is invoked on behalf of a technical user in context of the tenant. +::: + +::: details Node.js configuration explained + +The `kind` property activates the protocol for exchanging business data (same as `type` in Java). +The key in `cds.requires` must match the fully qualified CDS service name (e.g., `sap.capire.flights.data`), which is the same name used in `cds.connect.to()`. +The `credentials.url` provides the full endpoint URL including the service path `/hcql/data`. +The `forwardAuthToken: true` enables direct forwarding of the incoming JWT token to the provider service - this is suitable for co-located services that share the same identity instance, as the token is already valid for the provider. + +::: + ::: tip -On behalf of `systemUser` works both in pure single tenant and in pure multitenant scenarios. +On behalf of `systemUser` (Java) works both in pure single tenant and in pure multitenant scenarios. If you are consuming a single tenant service from within a multitenant application choose on behalf of `systemUserProvider`. ::: Now you are ready to deploy the application with -```sh +::: code-group +```sh [Java] cd ./xtravels_java cds up ``` +```sh [Node.js] +cd ./xtravels +cds up +``` +::: + ❗Note that CF application `xtravels-srv` will not start successfully as long as `xflights` is not deployed yet (step 3). ::: tip @@ -150,11 +192,12 @@ For different [user propagation](./cap-users#remote-services) modes the remote s The provider service authorization needs to align with the configured user propagation. ::: -Additionally, to establish the co-located setup, the microservice needs to share the same identity instance: +Additionally, to establish the co-located setup, the microservice needs to share the same identity instance. +This is configured in the MTA deployment descriptor (applies to both Java and Node.js): ::: code-group -```yaml [/srv/srv/main/resources/application.yaml] +```yaml [mta.yaml] resources: - name: xflights-ias type: org.cloudfoundry.managed-service # [!code --] @@ -172,11 +215,18 @@ resources: Finally, deploy and start the application with -```sh +::: code-group +```sh [Java] cd ./xflights_java cds up ``` +```sh [Node.js] +cd ./xflights +cds up +``` +::: + #### 4. Verify the deployment { #verify } First, you can check the overall deployment status at the CF CLI level. Specifically, the application services must be started successfully and the shared identity instance must be verified. @@ -220,7 +270,7 @@ As a consequence, external services can run cross-regionally; even non-BTP syste A prerequisite for external service calls is a trust federation between the consumer and the provider system. A seamless integration experience for external service communication is provided by [IAS App-2-App](#app-to-app) flows, which are offered by CAP via remote services. -Alternatively, remote services can be configured on top of [BTP HTTP Destinations](../services/consuming-services#using-destinations) that offer [various authentication strategies](https://help.sap.com/docs/connectivity/sap-btp-connectivity-cf/http-destinations) such as SAML 2.0 as required by many S/4 system endpoints. +[BTP HTTP Destinations](../services/consuming-services#using-destinations) offer [various authentication strategies](https://help.sap.com/docs/connectivity/sap-btp-connectivity-cf/http-destinations) such as SAML 2.0 as required by many S/4 system endpoints. Note that CAP Node.js uses BTP Destinations also for IAS App-2-App flows, while CAP Java handles IAS token exchange natively. ### IAS App-2-App { #app-to-app } @@ -248,7 +298,7 @@ CAP offers a simplified App-2-App setup by leveraging remote services that requi #### 1. Prepare and deploy the provider application -Assuming the same local CF environment setup as [here](#prepare), clone [`xflights-java`](https://github.com/capire/xflights-java/tree/main) or, if already cloned and modified locally, reset to the remote branch. +Assuming the same local CF environment setup as [here](#prepare), clone the sample application ([`xflights-java`](https://github.com/capire/xflights-java/tree/main) or [`xflights`](https://github.com/capire/xflights/tree/main) for Node.js), or if already cloned and modified locally, reset to the remote branch. Similar to the [co-located](#co-located-provider) variant, `xflights` needs to expose service `sap.capire.flights.data` to technical clients. The difference is that the consumers are not known a priori and are not part of the same application deployment. @@ -261,15 +311,37 @@ resources: - name: xflights-ias type: org.cloudfoundry.managed-service parameters: - [...] + service: identity + service-plan: application config: display-name: xflights - provided-apis: # [!code ++:5] + oauth2-configuration: + token-policy: + access-token-format: jwt + provided-apis: - name: data-consumer description: Grants technical access to data service API ``` ::: +For Node.js, additionally configure the authentication strategy in `package.json`: + +::: code-group +```json [Node.js: package.json] +{ + "cds": { + "requires": { + "auth": { + "[production]": { + "kind": "ias" + } + } + } + } +} +``` +::: + The entry with name `data-consumer` represents the consumption of service `sap.capire.flights.data` and is exposed as IAS API. The description helps administrators to configure the consumer application with the proper provider API if done on UI level. @@ -289,11 +361,18 @@ annotate data with @(requires: 'data-consumer'); Finally, deploy and start the application with -```sh +::: code-group +```sh [Java] cd ./xflights_java cds up ``` +```sh [Node.js] +cd ./xflights +cds up +``` +::: + ::: tip API as CAP role The API identifiers exposed by the IAS instance in list `provided-apis` are granted as CAP roles after successful authentication and can be used in @requires annotations. @@ -307,15 +386,15 @@ Instead of using the same role, expose dedicated CDS services to technical clien #### 2. Prepare and deploy the consumer application { #consumer } -Like with xflights, clone [`xtravels-java`](https://github.com/capire/xtravels-java/tree/main) or, if already cloned and modified locally, reset to remote branch. +Like with xflights, clone the sample application ([`xtravels-java`](https://github.com/capire/xtravels-java/tree/main) or [`xtravels`](https://github.com/capire/xtravels/tree/main) for Node.js), or if already cloned and modified locally, reset to remote branch. The remote service can be configured in a very similar way as with [co-located services](#co-located-consumer). -You only need to add the information about the IAS dependency to be called (`ias-dependency-name`). +You only need to add the information about the IAS dependency to be called. The name for the IAS dependency is flexible but **needs to match the chosen name in the next step** when [connecting consumer and provider in IAS](#connect). ::: code-group -```yaml [/srv/srv/main/resources/application.yaml] +```yaml [Java: application.yaml] spring: config.activate.on-profile: cloud cds: @@ -333,22 +412,117 @@ cds: ias-dependency-name: data-consumer ``` +```json [Node.js: package.json] +{ + "cds": { + "requires": { + "auth": { + "[production]": { + "kind": "ias" + } + }, + "sap.capire.flights.data": { + "kind": "hcql", + "[production]": { + "credentials": { + "path": "/hcql/data", + "destination": "xflights-api" + } + } + } + } + } +} +``` + ::: -Finally, deploy and start the application with +::: details Java configuration explained + +The `ias-dependency-name` property configures the IAS App-2-App flow directly in `application.yaml`. This is all that's needed for Java - the CAP Java runtime handles the token exchange automatically. + +::: + +::: details Node.js configuration explained + +Node.js uses BTP destinations for outbound authentication, which requires additional setup: +**1. MTA descriptor** - bind both IAS and destination services: + +```yaml +modules: + - name: xtravels-srv + [...] + requires: + - name: xtravels-ias + - name: xtravels-destination + +resources: + - name: xtravels-ias + type: org.cloudfoundry.managed-service + parameters: + service: identity + service-plan: application + config: + display-name: xtravels + oauth2-configuration: + token-policy: + access-token-format: jwt + + - name: xtravels-destination + type: org.cloudfoundry.managed-service + parameters: + service: destination + service-plan: lite +``` + +**2. SAP Cloud SDK dependencies** - required for destination-based authentication: + +```sh +npm add @sap-cloud-sdk/http-client @sap-cloud-sdk/connectivity @sap-cloud-sdk/resilience +``` + +See [Node.js: SAP Cloud SDK Requirement](#nodejs-cloud-sdk) for more details. + +**3. BTP Destination** - create `xflights-api` in BTP Cockpit (Connectivity → Destinations): + +- **Name**: `xflights-api` +- **Type**: HTTP +- **URL**: `https://` +- **ProxyType**: Internet +- **Authentication**: `OAuth2JWTBearer` +- **Client ID**: from `xtravels-ias` service key +- **Client Secret**: from `xtravels-ias` service key +- **Token Service URL**: `https://.accounts.ondemand.com/oauth2/token` + +To get the Client ID and Secret, create a service key: ```sh +cf create-service-key xtravels-ias xtravels-ias-key +cf service-key xtravels-ias xtravels-ias-key +``` + +::: + +Finally, deploy and start the application with + +::: code-group +```sh [Java] cd ./xtravels_java cds up ``` +```sh [Node.js] +cd ./xtravels +cds up +``` +::: + `xtravels-srv` is not expected to start successfully; instead, you should see error log messages like this: ```yaml Remote HCQL service responded with HTTP status code '401', ... ``` -Technically, the remote service implementation initiates an App-2-App flow. -It takes the token from the request and triggers an IAS token exchange for the target [IAS dependency](#connect) according to the user propagation strategy (technical communication here). +Technically, the remote service implementation initiates an App-2-App flow, taking the token from the request and triggering an IAS token exchange for the target [IAS dependency](#connect). As the IAS dependency is not created yet, IAS rejects the token exchange request and the call to the provider fails with `401` (not authenticated). Note that property `oauth2-configuration.token-policy.access-token-format: jwt` is set in the identity instance to ensure the exchanged token has JWT format. @@ -361,7 +535,7 @@ Open the Administrative Console for the IAS tenant (see prerequisites [here](./a 1. Select **Applications & Resources** > **Applications**. Choose the IAS application of the `xtravels` consumer from the list. 2. In **Application APIs** select **Dependencies** and click on **Add**. -3. Type `data-consumer` as dependency name (needs to match property value `ias-dependency-name`) and pick provided API `data-consumer` from the provider IAS application `xflights`. +3. Type `data-consumer` as dependency name (needs to match property value `ias-dependency-name` in Java, or the IAS dependency configured in the BTP destination for Node.js) and pick provided API `data-consumer` from the provider IAS application `xflights`. 4. Confirm with **Save** ::: details Create IAS dependency in Administrative Console @@ -390,6 +564,35 @@ To do so, assign a proper AMS policy (e.g., `admin`) to the test user as describ
+## Node.js: SAP Cloud SDK Requirement { #nodejs-cloud-sdk } + +CAP Node.js can handle remote service calls in two ways: + +**Without Cloud SDK** (native fetch): +- Direct URL with `forwardAuthToken: true` (co-located services) +- Direct URL with `BasicAuthentication` +- Local development scenarios + +**With Cloud SDK** (required): +- BTP destination-based authentication (including IAS App-2-App) +- OAuth2 token exchange scenarios +- On-premise connectivity via Cloud Connector + +::: tip Install SAP Cloud SDK +When using BTP destinations, add the SAP Cloud SDK packages to your project: +```sh +npm add @sap-cloud-sdk/http-client @sap-cloud-sdk/connectivity @sap-cloud-sdk/resilience +``` +::: + +The Cloud SDK provides: +- **BTP Destination resolution** with various authentication types, including IAS +- **Token exchange** when configured via BTP destinations +- **Resilience features** like timeout, retry, and circuit breaker + +[Learn more about SAP Cloud SDK connectivity features](https://sap.github.io/cloud-sdk/docs/js/features/connectivity/destinations){.learn-more} + + ## Pitfalls - **Don't write custom integration logic** for consumed services.