Search

Authentication

This guide is about authenticating users on incomming HTTP requests.

Configuration and Defaults

CDS ships with a few pre-built authentication strategies, which are switched on by default depending on your environment.

Defaults

The following default configurations apply based on the NODE_ENV:

Configuration

You can override the defaults by providing specific configuration to the cds.requires.auth configuration option in the cds section of your package.json or in .cdsrc.json. For example

"requires": {
  "auth": {
    "strategy": <String>,  //> to configure a pre-built one
    "impl": <String>,      //> for custom-defined one
  }
}

In addition you may add more properties depending on the chosen implementation, as documented in the sections below.

Introspection

Run cds env get requires.auth in your project root to find out the effectively configured authentication strategy for your current environment (i.e. development vs production).

Dummy Authentication

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

Configuration

In .cdsrc.json or cds section of package.json:

"requires": {
  "auth": {
    "strategy": "dummy"
  }
}

Mocked Authentication

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

Note: this mode must only be used during development. It’s disabled if the NODE_ENV environment variable is set to production.

Tip: 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

Default Configuration

The default configuration shipped with @sap/cds looks like that:

cds.requires.auth = {
  strategy: "mock",
  users: {
    "alice": { "roles": ["admin"] },
    "bob": { "roles": ["builder"] },
    "*": true //> all other logins are allowed as well
  }
}

Configuring Specific Users

You can override the users property in the cds section of your package.json or in .cdsrc.json as follows:

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

Learn more about Configuration in CAP Node.js

Token-Based Authentication (JWT)

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.

The steps below 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 you API endpoint, have a look at Regions.

  2. Go to your project you 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@^2 and the [@sap/xsenv][sap/xsenv] package:

     npm install @sap/xssec @sap/xsenv
    

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

Note that this step is necessary for locally running apps and for apps deployed on Cloud Foundry.

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.jsonfile 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"
         }
       }
     }
    

    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.

    Learn more about how this matching to VCAP_SERVICES works

  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 and created the service binding to XSUAA, you enter the SAP Cloud Platform 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 Cloud Platform 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 to 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 go to 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 IDP 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 Cloud Platform 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 Cloud Platform 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"

Custom-defined Authentication

In case the supported authentication strategies do not fulfill the necessary requirements, developers can provide a custom express middleware for authentication.

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.

Configuration

You can configure an own implementation in .cdsrc.json or the cds section of your package.json as follows:

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

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 correspond to $user in @restrict annotations of your CDS models ( Also in JavaScript, user can act as a shortcut for user.id in comparisons. )

user.locale : string

The user’s preferred locale. cds.Users implements that as a getter that will resolve to the normalized Accept-Language header of incomming requests.

user.tenant : string

The user’s tenant id, or undefined if not run in multi-tenancy mode.

user.attr.<> : string

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

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.

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()
})