-
Notifications
You must be signed in to change notification settings - Fork 158
Add Node.js part to Outbound Authentication #2596
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
4ec8fef
3696aa2
1223b4d
1989b74
f4707e0
37814e3
786b0d5
40595d7
600b1ac
e5abd9b
8691254
3cc3597
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 | |
| {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: | ||
|
|
@@ -86,9 +90,7 @@ Make sure that you've prepared a [local environment for CF deployments](../deplo | |
|
|
||
| As client, `xtravels-srv` first needs a valid configuration for the remote service `sap.capire.flights.data`: | ||
|
|
||
| ::: code-group | ||
|
|
||
| ```yaml [/srv/src/main/resources/application.yaml] | ||
| ```yaml [Java: application.yaml] | ||
| --- | ||
| spring: | ||
| config.activate.on-profile: cloud | ||
|
|
@@ -105,26 +107,62 @@ cds: | |
| options: | ||
| url: https://<xflights-srv-cert url> | ||
| ``` | ||
| ::: | ||
|
|
||
| ::: 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. | ||
|
|
||
| ::: | ||
|
|
||
| ::: 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`. | ||
| ::: | ||
|
|
||
| ```json [Node.js: package.json] | ||
| { | ||
| "cds": { | ||
| "requires": { | ||
| "sap.capire.flights.data": { | ||
| "kind": "hcql", | ||
| "[production]": { | ||
| "credentials": { | ||
| "url": "https://<xflights-srv-cert url>/hcql/data", | ||
| "forwardAuthToken": true | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ::: details Node.js configuration explained | ||
|
|
||
| The configuration follows the standard pattern for [required services](../integration/reuse-and-compose#configuring-required-services) with [service bindings](../integration/reuse-and-compose#binding-required-services). | ||
|
|
||
| For co-located services sharing the same identity instance, `forwardAuthToken: true` forwards the incoming JWT directly to the provider - no token exchange needed since the token is already valid. Unlike Java's `onBehalfOf` option, no additional configuration is required as the original user context is preserved in the forwarded token. | ||
|
|
||
| ::: | ||
|
|
||
| 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 +188,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: | ||
|
|
||
| ::: code-group | ||
|
|
||
| ```yaml [/srv/srv/main/resources/application.yaml] | ||
| ```yaml [mta.yaml] | ||
| resources: | ||
| - name: xflights-ias | ||
| type: org.cloudfoundry.managed-service # [!code --] | ||
|
|
@@ -172,11 +211,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 | ||
| ``` | ||
| ::: | ||
|
|
||
|
PDT42 marked this conversation as resolved.
|
||
| #### 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 +266,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 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. Both CAP Java and CAP Node.js support IAS App-2-App via configuration to handle token exchange automatically - Java uses service bindings with `ias-dependency-name`, while Node.js uses BTP Destinations with `tokenService.body.resource`. | ||
|
|
||
|
|
||
| ### IAS App-2-App { #app-to-app } | ||
|
|
@@ -235,7 +281,7 @@ Technically, the connectivity component uses [IAS App-2-App flows](https://help. | |
| The latter is issued by IAS only if the consumer is configured with a valid IAS dependency pointing to the provider accordingly. | ||
|
|
||
| :::tip | ||
| CAP offers a simplified App-2-App setup by leveraging remote services that require: | ||
| CAP offers App-2-App setup by leveraging remote services that require: | ||
| - Identity instances for provider and consumer | ||
| - Configured IAS dependency from consumer to provider | ||
| - URL pointing to the provider | ||
|
|
@@ -248,7 +294,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,12 +307,34 @@ resources: | |
| - name: xflights-ias | ||
| type: org.cloudfoundry.managed-service | ||
| parameters: | ||
| [...] | ||
| service: identity | ||
| service-plan: application | ||
| config: | ||
| display-name: xflights | ||
| provided-apis: # [!code ++:5] | ||
| - name: data-consumer | ||
| description: Grants technical access to data service API | ||
| 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" | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
| ::: | ||
|
|
||
|
|
@@ -289,11 +357,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 +382,13 @@ 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 +406,127 @@ cds: | |
| ias-dependency-name: data-consumer | ||
| ``` | ||
|
|
||
| ::: 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. | ||
|
|
||
| ::: | ||
|
|
||
| **Node.js:** Configure a BTP Destination that handles the IAS token exchange. The destination references the IAS dependency name, which **must match** the name used when [connecting consumer and provider in IAS](#connect). | ||
|
|
||
| ```json [Node.js: package.json] | ||
|
PDT42 marked this conversation as resolved.
|
||
| { | ||
| "cds": { | ||
| "requires": { | ||
| "auth": { | ||
| "[production]": { | ||
| "kind": "ias" | ||
| } | ||
| }, | ||
| "sap.capire.flights.data": { | ||
| "kind": "hcql", | ||
| "[production]": { | ||
| "credentials": { | ||
| "path": "/hcql/data", | ||
| "destination": "xflights-ias-app2app" | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ::: details Node.js configuration explained | ||
|
|
||
| CAP Node.js supports IAS App-2-App via BTP Destinations using standard remote service configuration. | ||
|
|
||
| **1. Configuration** - Use a named destination in `credentials`: | ||
| - `path`: The HCQL endpoint path on the provider | ||
| - `destination`: Name of the BTP Destination configured for IAS App-2-App | ||
|
|
||
| **2. BTP Destination** - Create a destination in BTP Cockpit with these properties: | ||
|
|
||
| | Property | Value | | ||
| |----------|-------| | ||
| | Name | `xflights-ias-app2app` | | ||
| | Type | `HTTP` | | ||
| | URL | `https://<xflights-srv url>` | | ||
| | Proxy Type | `Internet` | | ||
| | Authentication | `OAuth2ClientCredentials` or `OAuth2JWTBearer` | | ||
| | Client ID | Consumer IAS client ID | | ||
| | Client Secret | Consumer IAS client secret | | ||
|
Comment on lines
+457
to
+458
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where do people get these?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. info added
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Conceptually, should the destination be using the creds from the service binding or a dedicated service key? |
||
| | Token Service URL | `https://<IAS tenant>/oauth2/token` | | ||
| | Token Service URL Type | `Dedicated` | | ||
|
|
||
| Client ID and Client Secret are obtained from the consumer's IAS service binding (e.g., via `cf env xtravels-srv` or BTP Cockpit). | ||
|
|
||
| **Additional Property (required for IAS App-2-App):** | ||
|
|
||
| | Property | Value | | ||
| |----------|-------| | ||
| | `tokenService.body.resource` | `urn:sap:identity:application:provider:name:data-consumer` | | ||
|
|
||
| The key is `tokenService.body.resource` which passes the `resource` parameter to IAS, triggering App-2-App token scoping and adding the `ias_apis` claim. | ||
|
PDT42 marked this conversation as resolved.
|
||
|
|
||
| **3. IAS App-2-App supports two authentication types:** | ||
| - **OAuth2ClientCredentials**: For technical user scenarios (no user context) | ||
| - **OAuth2JWTBearer**: For user propagation (requires user token from authorization_code flow) | ||
|
|
||
| Both support `tokenService.body.resource` for IAS App-2-App scoping. | ||
|
|
||
| **4. MTA descriptor** - bind the 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 | ||
| ``` | ||
|
|
||
| **Note:** MTA cannot resolve cross-service credential references like `${generated>xtravels-ias/clientid}`. The destination must be created manually in BTP Cockpit or via Destination Service API. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps: Move this to the beginning of "2. BTP Destination" or right before "Client ID and Client Secret are obtained from the consumer's ..."? Provide the reason for what should be done, before doing it? |
||
|
|
||
| ::: | ||
|
|
||
| Finally, deploy and start the application with | ||
|
|
||
| ```sh | ||
| ::: 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 App-2-App flow takes the token from the request and triggers an IAS token exchange for the target [IAS dependency](#connect). In Java, CAP's remote service handles this automatically. In Node.js, the BTP Destination with `tokenService.body.resource` triggers the token exchange via the Destination Service. | ||
| 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 +539,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 name suffix in `tokenService.body.resource` URN for Node.js, e.g., `urn:sap:identity:application:provider:name:data-consumer`) and pick provided API `data-consumer` from the provider IAS application `xflights`. | ||
| 4. Confirm with **Save** | ||
|
|
||
| ::: details Create IAS dependency in Administrative Console | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.