SAP Fiori, SAPUI5

Secure Fiori App in SAP BTP with XSUAA

Requirement is to have Managed App Router based Fiori app in SAP BTP – CF which is calling OData/web service(We have taken example of Northwind for this project) and add additional authorization layer with the help of XSUAA to achieve in app authorization.

Here Node JS API and HTML5 modules are added in single MTA sharing instance of XSUAA

and communicating with the help of OAuth2UserTokenExchange type instance level destination.

Note: Destinations are created with help of MTA.yaml dependencies.

You can find complete project at GitHub

Step1: Create Basic MTA

Step2: Add Managed App Router

Right click on MTA.yaml and select create MTA Module from Template

Step3: Add Fiori/SAPUI5 Module in MTA

Step4: Add Node JS Module to add additinal layer for calling web service/OData.

Add Node JS module in MTA04 project to call Northwind service and add XSUAA Auth. Refer GitHub Project.

Below code you can refer for Node JS API which is calling Northwind service. Folder structure can be referred from Git project shared above.

const express = require("express");
const fetch = require("axios");
const app = express();
const port = process.env.port || 8080;
const { JWTStrategy } = require("@sap/xssec");
const xsenv = require("@sap/xsenv");
const passport = require("passport");

passport.use(new JWTStrategy(xsenv.getServices({ uaa: { tag: "xsuaa" } }).uaa));

app.use(passport.initialize());
app.use(passport.authenticate("JWT", { session: false }));

app.get("/", (req, res, next) => {
  res.send("Node Js Based Service With App Router for SAP BTP CF by Satyajit");
});

app.get("/PRD/Products", checkScope, async (req, res, next) => {
  var result;
  result = await fetch.get("https://services.odata.org/v2/northwind/northwind.svc/Products?$format=json")
  res.send(result.data);
});


function checkScope(req, res, next) {
  if (req.authInfo.checkLocalScope("read")) {
    next();
  } else {
    res.status(403).end("Forbidden");
  }
}
app.listen(port, console.log(`Listening on port ${port}`));

Step5: Make AJAX call to Node JS service.

sap.ui.define([
    "sap/ui/core/mvc/Controller"
],
    /**
     * @param {typeof sap.ui.core.mvc.Controller} Controller
     */
    function (Controller) {
        "use strict";

        return Controller.extend("ns.mta04.mta04.controller.View1", {
            onInit: function () {
                let path = this.getOwnerComponent().getManifestObject()._oBaseUri._parts.path;
                var sUrl = path + "PRD/Products";
                //var oView = this.getView();
                var oTextModel = this.getOwnerComponent().getModel("textModel");
                //this.getView().setModel(oTextModel,"textModel");

                var oTable = this.getView().byId("idTabel01");

                jQuery.ajax({
                    url: sUrl,
                    method: "GET",
                    success: function (oResponse) {
                        debugger;
                        oTextModel.setData(oResponse.d.results);
                        oTable.setModel(oTextModel);
                        oTable.bindRows("textModel>/");
                        
                    },
                    error: function () { 
                        debugger;
                    }
                });
            }
        });
    });

View1.view.xml code

<mvc:View xmlns:mvc="sap.ui.core.mvc" xmlns:m="sap.m" xmlns="sap.ui.table" controllerName="ns.mta04.mta04.controller.View1" displayBlock="true">
    <m:Page id="page" title="{i18n>title}">
        <m:content>
<Table enableCellFilter="true" selectionMode="None"  visibleRowCount="15" id="idTabel01" rows="{textModel>/}">
                                <columns>
                                    <Column id="c1" width="25%">
                                        <m:Label text="Product ID" id="l1"/>
                                        <template>
                                            <m:Text text="{textModel>ProductID}" id="t1" />
                                        </template>
                                    </Column>
                                    <Column width="50%" id="c2">
                                        <m:Label text="Product Name" id="l2" />
                                        <template>
                                            <m:Text text="{textModel>ProductName}" id="t2" />
                                        </template>
                                    </Column>
                                     <Column width="25%" id="c3">
                                        <m:Label text="Unit Price" id="l3" />
                                        <template>
                                            <m:Text text="{textModel>UnitPrice}" id="t3" />
                                        </template>
                                    </Column>
                                    </columns>
                               
                            </Table>
        </m:content>
    </m:Page>
</mvc:View>

Adjust MTA.yaml as below.

_schema-version: "3.2"
ID: MTA04
version: 0.0.1
modules:
- name: API_NodeJS
  type: nodejs
  path: ./API_WITH_XSUAA
  requires:
  - name: uaa_MTA04
  provides:
  - name: srv-api-nodejs
    properties:
      srv-url: ${default-url}
  parameters:
    buildpack: nodejs_buildpack
  build-parameters:
    builder: npm-ci
- name: MTA04-destination-content
  type: com.sap.application.content
  requires:
  - name: MTA04-destination-service
    parameters:
      content-target: true
  - name: MTA04_html_repo_host
    parameters:
      service-key:
        name: MTA04_html_repo_host-key
  - name: srv-api-nodejs         
  - name: uaa_MTA04
    parameters:
      service-key:
        name: uaa_MTA04-key
  parameters:
    content:
      instance:
        destinations:
        - Authentication: OAuth2UserTokenExchange
          Name: api-nodejs-srv
          TokenServiceInstanceName: MTA04-xsuaa-service
          TokenServiceKeyName: uaa_MTA04-key
          URL: ~{srv-api-nodejs/srv-url}        
        - Name: MTA04_MTA04_html_repo_host
          ServiceInstanceName: MTA04-html5-app-host-service
          ServiceKeyName: MTA04_html_repo_host-key
          sap.cloud.service: MTA04
        - Authentication: OAuth2UserTokenExchange
          Name: MTA04_uaa_MTA04
          ServiceInstanceName: MTA04-xsuaa-service
          ServiceKeyName: uaa_MTA04-key
          sap.cloud.service: MTA04
        existing_destinations_policy: ignore
  build-parameters:
    no-source: true
- name: MTA04-app-content
  type: com.sap.application.content
  path: .
  requires:
  - name: MTA04_html_repo_host
    parameters:
      content-target: true
  build-parameters:
    build-result: resources
    requires:
    - artifacts:
      - nsmta04mta04.zip
      name: nsmta04mta04
      target-path: resources/
- name: nsmta04mta04
  type: html5
  path: mta04
  build-parameters:
    build-result: dist
    builder: custom
    commands:
    - npm install
    - npm run build:cf
    supported-platforms: []
resources:
- name: MTA04-destination-service
  type: org.cloudfoundry.managed-service
  parameters:
    config:
      HTML5Runtime_enabled: true
      init_data:
        instance:
          destinations:
          - Authentication: NoAuthentication
            Name: ui5
            ProxyType: Internet
            Type: HTTP
            URL: https://ui5.sap.com
          existing_destinations_policy: update
      version: 1.0.0
    service: destination
    service-name: MTA04-destination-service
    service-plan: lite
- name: MTA04_html_repo_host
  type: org.cloudfoundry.managed-service
  parameters:
    service: html5-apps-repo
    service-name: MTA04-html5-app-host-service
    service-plan: app-host
- name: uaa_MTA04
  type: org.cloudfoundry.managed-service
  parameters:
    path: ./xs-security.json
    service: xsuaa
    service-name: MTA04-xsuaa-service
    service-plan: application
parameters:
  deploy_mode: html5-repo

Step6: Deploy app to CF and add add to FLP service.

Right click on MTA.YAML and click on Build MTA Project.

Add Fiori Application to Fiori Launchpad service

** If Launchpad service is not available create new subscription.

Add App to group and Role. It will be available in FLP service.

Also app can be tested form HTML5 repo