Search

    Authentication

    This guide is about authenticating users on incoming HTTP requests. This is done by authentication middlewares setting the req.user property which is then used in authorization enforcement decisions.

    req.user cds.User class

    Represents the currently logged-in user as filled into req.user by authentication middlewares. Simply create instances of cds.User or of subclasses thereof in custom middlewares. For example:

    const cds = require('@sap/cds')
    const DummyUser = new class extends cds.User { is:()=>true }
    module.exports = (req,res,next) => {
      req.user = new DummyUser('dummy')
      next()
    }
    

    Properties & Methods

    user.id : string

    A user’s unique ID. It corresponds to $user in @restrict annotations of your CDS models (Also in JavaScript, user can act as a shortcut for user.id in comparisons.)

    user.is (<role>) boolean

    Checks if user has assigned the given role. Example usage:

    if (req.user.is('admin')) ...
    

    The role names correspond to the values of @requires and the @restrict.grants.to annotations in your CDS models.

    user.attr.<x> : string

    User-related attributes, for example, from JWT tokens These correspond to $user.<x> in @restrict annotations of your CDS models

    DEPRECATED: user.tenant : string

    Use req/msg.tenant instead.

    DEPRECATED: user.locale : string

    Use req/msg.locale instead.

    cds.User.Privileged class

    In some cases, you might need to bypass authorization checks while consuming a local service. For this, you can create a transaction with a privileged user as follows:

    this.before('*', async function (req) {
      // pass tenant if needed
      const user = new cds.User.Privileged({ tenant: 'myTenant' })
      const tx = this.transaction({ user })
      await tx.run(INSERT.into('RequestLog').entries({
        url: req._.req.url,
        user: req.user.id
      }))
      await tx.commit()
    })
    

    Authorization Enforcement

    Applications can use the req.user APIs to do programmatic enforcement. For example, the authorization of the following CDS service:

    service CustomerService @(requires: 'authenticated-user'){
      entity Orders @(restrict: [ 
        { grant: ['READ','WRITE'], to: 'admin' },
      ]){/*...*/}
      entity Approval @(restrict: [
        { grant: 'WRITE', where: '$user.level > 2' } 
      ]){/*...*/}
    }
    

    can be programmatically enforced by means of the API as follows:

    const cds = require('@sap/cds')
    cds.serve ('CustomerService') .with (function(){
      this.before ('*', req =>
       req.user.is('authenticated') || req.reject(403)
      )
      this.before (['READ', 'CREATE'], 'Orders', req =>
        req.user.is('admin') || req.reject(403)
      )
      this.before ('*', 'Approval', req =>
        req.user.attr.level > 2 || req.reject(403)
      )
    })
    

    Authentication Strategies

    CAP ships with a few prebuilt authentication strategies, used by default: basic-auth during development and jwt-auth in production. You can override these defaults and configure the authentication strategy to be used through the cds.requires.auth config option in cds.env, for example:

    "cds": { // in package.json
      "requires": { 
        "auth": { "kind": "jwt-auth" }
      }
    }
    

    Run cds env get requires.auth in your project root to find out the effective authentication config for your current environment.

    Dummy Authentication

    This strategy creates a user that passes all authorization checks. It’s meant for temporarily disabling the @requires and @restrict annotations at development time.

    Configuration: Choose this strategy as follows:

    "cds": { // in package.json
      "requires": {
        "auth": { "kind": "dummy-auth" }
      }
    }
    

    Basic Authentication

    This authentication strategy uses passport with basic authentication to use mock users during development.

    Note: When testing different users in the browser, it’s best to use an incognito window, because logon information might otherwise be reused.

    Prerequisites: You need to add passport to your project:

    npm add passport
    

    Configuration: Choose this strategy as follows:

    "cds": { // in package.json
      "requires": {
        "auth": { "kind": "basic-auth" }
      }
    }
    

    You can optionally configure users as follows:

    "cds": { // in package.json
      "requires": {
        "auth": {
          "kind": "basic-auth",
          "users": {
            "<user.id>": { 
              "password": "<password>", 
              "roles": [ "<role-name>", ... ],
              "userAttributes": { ... }
            },
          }
        }
      }
    }
    

    The default configuration shipped with @sap/cds specifies these users:

      "users": {
        "alice": { "roles": ["admin"] },
        "bob": { "roles": ["builder"] },
        "*": true //> all other logins are allowed as well
      }
    }
    

    This default configuration is merged with your custom configuration such that, by default, logins by alice, bob, and others (*) are allowed.

    If you want to restrict these additional logins, you need to overwrite the defaults:

      "users": {
        "alice": { "roles": [] },
        "bob": { "roles": [] },
        "*": false //> do not allow other users than the ones specified
      }
    

    JWT-based Authentication

    This is the strategy to be used in production. User identity, as well as assigned roles and user attributes, are provided at runtime, by a bound instance of the ‘user account and authentication’ service (UAA). This is done in form of a JWT token in the Authorization header of incoming HTTP requests.

    Prerequisites: You need to add [@sap/xssec] to your project:

    npm add @sap/xssec
    

    Configuration: Choose this strategy as follows:

    "cds": { // in package.json
      "requires": {
        "auth": { "kind": "jwt-auth" }
      }
    }
    

    See XSUAA in Hybrid Setup below for additional information of how to test this

    XSUAA-based Authentication

    Authentication kind xsuaa is a logical extension of kind JWT that additionally offers access to SAML attributes through req.user.attr (for example, req.user.attr.familyName).

    Prerequisites: You need to add [@sap/xssec] to your project:

    npm add @sap/xssec
    

    Configuration: Choose this strategy as follows:

    "cds": { // in package.json
      "requires": {
        "auth": { "kind": "xsuaa-auth" }
      }
    }
    

    See XSUAA in Hybrid Setup below for additional information of how to test this

    It’s recommended to only use this authentication kind if it’s necessary for your use case, as it denotes a lock-in to SAP BTP.

    Custom Authentication

    You can configure an own implementation by specifying an own impl as follows:

    "requires": {
      "auth": {
        "impl": "srv/custom-auth.js" // > relative path from project root
      }
    }
    

    Essentially, custom authentication middlewares must fulfill the req.user contract by assigning an instance of cds.User or a look-alike to the incoming request at req.user.

    XSUAA in Hybrid Setup

    Prepare Local Environment

    The following steps assume you’ve set up the Cloud Foundry Command Line Interface.

    1. Log in to Cloud Foundry:

      cf l -a <api-endpoint>
      

      If you don’t know the API endpoint, have a look at section Regions and API Endpoints Available for the Cloud Foundry Environment.

    2. Go to the project you have created in Getting started in a Nutshell.

    3. Start the watch process on your project:

       cds watch
      
    4. Enable the strategy by installing the @sap/xssec package:

       npm install @sap/xssec 
      

    Next, you need to bind and configure the UAA service.

    Create XSUAA Configuration

    1. Create a folder called gen and compile your CDS model with authentication annotations into a full xs-security.json:

      mkdir gen
      cds compile srv/ --to xsuaa > gen/xs-security.json
      

      Note how in xs-security.json the admin scope from the CDS model has materialized as a scope and a role template.

    2. Create an XSUAA service with this configuration:

      cf create-service xsuaa application bookshop-uaa -c gen/xs-security.json
      

      Later on, if you’ve changed the scopes, you can use cf update-service bookshop-uaa -c xs-security.json to update the configuration.

      Troubleshooting: Invalid JSON

    This step is necessary for locally running apps and for apps deployed on Cloud Foundry.

    If you’re using SAP Business Application Studio as your development environment, you need to add another property to the xs-security.json to configure which redirect URIs are allowed.

    The format is the following:

    "oauth2-configuration": {
        "redirect-uris": [
        "https://[<dev-space-identifier>].[<landscape>].applicationstudio.cloud.sap/"
      ]
    }
    

    There are three options for the configuration:

    • Use the full URL of your application, which is the most secure option. This is a specific configuration for your dev space and should not be submitted or shared.
      • Get the URL using Ports:Preview from the command palette.
    • Use a partial URL containing the landscape and the host (for example: https://trial.applicationstudio.cloud.sap/) or only the host (for example: https://applicationstudio.cloud.sap/).

    See section Application Security Descriptor Configuration Syntax for more details on configuration options.

    Configure the Application

    1. Create a default-env.json file in the root of your project and insert this code:

       "VCAP_SERVICES": {
         "xsuaa": [{
           "name": "bookshop-uaa",
           "label": "xsuaa",
           "tags": [ "xsuaa" ],
           "credentials": { ... }
         }]
       }
      
    2. Create a service key:

      cf create-service-key bookshop-uaa bookshop-uaa-key
      cf service-key bookshop-uaa bookshop-uaa-key
      

      You do this, to gain access to the XSUAA credentials.

    3. Copy the JSON snippet from the console into the default-env.json file in the VCAP_SERVICES.xsuaa.credentials block.

      This step is only necessary if your application is running locally.

    4. Enhance your app’s configuration in package.json by a uaa section inside the cds.requires block:

       "cds": {
         "requires": {
           "uaa": { "kind": "xsuaa-auth" }
         }
       }
      

      This configuration, together with the credentials from default-env.json, is used by the Node.js runtime to validate the JWT token. To verify it, you can run cds env list requires.uaa, which prints the full uaa configuration including the credentials.

    5. Install node modules that are required at runtime to authenticate the user and to read the JWT token:

      npm install --save passport @sap/xssec @sap/audit-logging
      

    Set Up the Roles for the Application

    Once you’ve deployed your application to the Cloud Foundry environment for SAP BTP and created the service binding to XSUAA, you enter the SAP BTP Cockpit. In the cockpit, you set up the roles and role collections and assign the role collections to your users. This brings the necessary authorization information into the JWT token when the user logs on to your application through XSUAA and approuter.

    Since XSUAA configuration in cloud cockpit only works on existing applications, you need to temporarily push the app and bind it to the previously created service. You don’t need the app to be running at this stage.

    1. Push the application without start:

      cf target  # make sure that you're logged in to the correct org and space
      cf push bookshop --no-start --no-manifest --random-route
      
    2. Bind your application to your xsuaa service instance:

      cf bind-service bookshop bookshop-uaa
      
    3. Open the SAP BTP Cockpit.

      For your trial account, this is: https://cockpit.hanatrial.ondemand.com

    4. Create a role collection.

      The roles collections are created on subaccount level in the cockpit. Navigate to your subaccount and then choose Security > Role Collections. Create role collections

    5. Create roles and add them to your role collection.

      The roles are created on application level in the cockpit and based on your role templates. Navigate to the application in the correct space in your subaccount and then choose Security > Roles. The wizard takes you through all needed steps. Remember to include your role to the role collection you created before. Create roles

    6. Assign the role collections to users.

      The user role assignment is done in the Trust Configuration of your subaccount. Select the identity provider where the user is authenticated. Assign role collection to user. Select IDP Enter the E-mail address and select Show Assignments, to see already existing assignments. Then select Assign Role Collection to add your user to the role collection. Assign role collection to user. Select user

      See Assign Role Collections in SAP BTP documentation for more details.

    Running Approuter

    The approuter component implements the necessary handshake with XSUAA to let the user log in interactively. The resulting JWT token is sent to the application where it’s used to enforce authorization.

    1. Create a file app/package.json with the following content:

      {
        "name": "approuter",
        "dependencies": {
       "@sap/approuter": "^8"
        },
        "scripts": {
       "start": "node node_modules/@sap/approuter/approuter.js"
        }
      }
      
    2. Create the approuter configuration file app/xs-app.json with the following content:

      {
        "routes": [{
       "source": "^/(.*)",
       "destination": "srv_api"
       }]
      }
      
    3. Create a file app/default-env.json with the following content:

      {
       "destinations" : [
         {
           "name": "srv_api",
           "url": "<service-url>",
           "forwardAuthToken": true
         }
       ]
      }
      

      where

      • srv_api is the destination name from xs-app.json.
      • <service-url> is the service root of your app, for example, http://localhost:4004
    4. Copy the VCAP_SERVICES block of file default-env.json into file app/default-env.json. This tells approuter which UAA instance to contact.

    5. In app/ folder, run:

      npm install  # install approuter modules
      npm start    # start approuter
      

      This starts an approuter instance on http://localhost:5000

      Since it only serves static files or delegates to the backend service, you can keep the server running. It doesn’t need to be restarted after you have changed files.

    6. After the approuter is started, log in at http://localhost:5000 and verify that the routes are protected as expected. In our example, if you assigned the admin scope to your user in SAP BTP cockpit, you can now access http://localhost:5000/admin.


    To test UIs w/o a running UAA service, just add this to xs-app.json: "authenticationMethod": "none"

    Show/Hide Beta Features