cancel
Showing results for 
Search instead for 
Did you mean: 

BTP server-to-server communication in background jobs with same XSUAA

kosmopilot
Explorer
0 Kudos
149

On the BTP (CF Runtime) I have two servers (CAP NodeJs apps) that are bound to the same XSUAA instance. I would like them to communicate in background jobs (server-to-server). This currently fails on the BTP (403) as the endpoints require app-specific roles which are not included in the token obtained by the OAuth2 ClientCredentials grant flow. According to the xs-security.json documentation scopes may be granted to "authorities" when using the ClientCredentials grant flow. How do I modify the single xs-security.json (see below) used by the XSUAA to make it work?

An example repo can be found here . The explanation is as follows.

The scenario contains two servers: producer and consumer. Once the producer server is served, a background job is started which periodically POSTs the `consume` endpoint of consumer that requires the role "Admin". 

 

// simplified producer init()
    cds.once("served", () => {
      cds.spawn({ every: INTERVAL }, async () => {
        const msg = `Message #${++msgCount}`;

        cds.log("Producer").log("Producing: ", msg);

        const consumerSrv = await cds.connect.to("ConsumerService");
        await consumerSrv.send("consume", {
          msg: msg,
        });
      });
    });

 

service ConsumerService @(path: '/api/consumer/') {
   @(requires: ['Admin'])
   action   consume(msg : String);
}
// consumer init()
  public async init(): Promise<void> {
    await super.init();

    const ConsumerService = await import("../@cds-models/ConsumerService");
    this.on(ConsumerService.consume, (req) => {
      cds.log("Consumer").log("Consuming message of user ", req.user?.id, " : ", req.data.msg);
    });
  }

 

The cds.requires section of producer looks like this:

  "requires": {
    "ConsumerService": {
      "kind": "odata",
      "model": "example.consumer",
      "credentials": {
        "path": "/api/consumer/",
        "[production]": { "destination": "consumer-api" },
        "[development]": {
          "destination": {
            "name": "consumer-api",
            "url": "http://localhost:4005",
            "username": "admin",
            "password": "admin"
          }
        }
      }
    },
    "auth": {
      "[production]": { "kind": "xsuaa" },
      "[development]": {
        "kind": "basic",
        "users": {
          "admin": {
            "password": "admin",
            "roles": ["Admin"]
          }
        }
      }
    }
  },

 

During deployment the destination "consumer-api" is created. The manifest.yaml looks like this:

_schema-version: "3.1"
ID: backgroundjobs
version: 1.0.0
description: "Example project for server-to-server communication in background jobs"
parameters:
  enable-parallel-deployments: true

modules:
  # --------------------- PRODUCER MODULE ------------------------
  - name: backgroundjobs-producer
    # ------------------------------------------------------------
    type: nodejs
    path: ../dist/srv/producer
    parameters:
      buildpack: nodejs_buildpack
      memory: 128MB
      disk-quota: 128MB
      command: node ./node_modules/@sap/cds/bin/cds-serve --profile production
    properties:
      OPTIMIZE_MEMORY: true
    build-parameters:
      builder: custom
      commands: []
    requires:
      - name: backgroundjobs-uaa
      - name: backgroundjobs-destination
      - name: backgroundjobs-consumer
    provides:
      - name: producer-api
        properties:
          srv-url: ${default-url}

  # --------------------- CONSUMER MODULE ------------------------
  - name: backgroundjobs-consumer
    # ------------------------------------------------------------
    type: nodejs
    path: ../dist/srv/consumer
    parameters:
      buildpack: nodejs_buildpack
      memory: 128MB
      disk-quota: 128MB
      command: node ./node_modules/@sap/cds/bin/cds-serve --profile production
    properties:
      OPTIMIZE_MEMORY: true
    build-parameters:
      builder: custom
      commands: []
    requires:
      - name: backgroundjobs-uaa
      - name: backgroundjobs-destination
    provides:
      - name: consumer-api
        properties:
          srv-url: ${default-url}

  # ---------------------- DESTINATIONS --------------------------
  - name: backgroundjobs-destination-content
    # ------------------------------------------------------------
    type: com.sap.application.content
    build-parameters:
      no-source: true
    requires:
      - name: backgroundjobs-destination
        parameters:
          content-target: true
      - name: consumer-api
      - name: backgroundjobs-uaa
        parameters:
          service-key:
            name: ${org}-${space}-backgroundjobs-uaa-key
    parameters:
      content:
        instance:
          existing_destinations_policy: update
          destinations:
            - Name: consumer-api
              URL: ~{consumer-api/srv-url}
              WebIDEEnabled: true
              Authentication: OAuth2ClientCredentials
              HTML5.ForwardAuthToken: false # when enabled, the ClientCredentials flow is not triggered and we need that in background jobs
              TokenServiceURLType: Dedicated
              TokenServiceInstanceName: ${org}-${space}-backgroundjobs-uaa
              TokenServiceKeyName: ${org}-${space}-backgroundjobs-uaa-key

resources:
  - name: backgroundjobs-uaa
    type: org.cloudfoundry.managed-service
    parameters:
      service-name: ${org}-${space}-backgroundjobs-uaa
      service: xsuaa
      service-plan: application
      path: ./xs-security.json
      config:
        xsappname: ${org}-${space}-backgroundjobs
      service-keys:
        - name: ${org}-${space}-backgroundjobs-uaa-key
  - name: backgroundjobs-destination
    type: org.cloudfoundry.managed-service
    parameters:
      service-name: ${org}-${space}-backgroundjobs-destination
      service: destination
      service-plan: lite

 

And the xs-security.json:

{
  "xsappname": "${default-xsappname}",
  "tenant-mode": "dedicated",
  "scopes": [
    {
      "name": "$XSAPPNAME.Admin"
    }
  ],
  "attributes": [],
  "role-templates": [
    {
      "name": "Admin",
      "description": "",
      "scope-references": ["$XSAPPNAME.Admin"],
      "attribute-references": []
    }
  ],
  "oauth2-configuration": {
    "redirect-uris": ["https://*.hana.ondemand.com/**"]
  }
}

  

Accepted Solutions (1)

Accepted Solutions (1)

kosmopilot
Explorer
0 Kudos

Answers (0)