53 · Mock Authentication

Configure .cdsrc.json mock users with roles and attributes. Write positive and negative auth tests with cds.test().

Episode 53 — Mock Authentication in Development

What you'll learn

Why this matters

Every developer needs to test auth scenarios locally — without an XSUAA service instance, without network latency, without shared credentials. The mocked auth system gives you a production-faithful simulation that runs entirely offline.

Full mock configuration

// .cdsrc.json
{
  "requires": {
    "auth": {
      "[development]": {
        "kind": "mocked",
        "users": {
          "alice": {
            "ID":       "alice",
            "password": "alice",
            "roles":    ["Buyer"],
            "attr": {
              "region":     "EMEA",
              "costCenter": "CC-1001"
            },
            "tenant": "tenant-A"
          },
          "bob": {
            "ID":       "bob",
            "password": "bob",
            "roles":    ["Admin","Buyer"],
            "attr": {
              "region":     "NA"
            },
            "tenant": "tenant-A"
          },
          "carol-analyst": {
            "ID":       "carol",
            "password": "carol",
            "roles":    ["Analyst"],
            "tenant":   "tenant-B"
          },
          "*": {
            "roles": [],
            "tenant": "tenant-default"
          }
        }
      },
      "[production]": {
        "kind": "xsuaa"
      }
    }
  }
}

"*" defines the default user — anyone with an unknown username gets these settings.

HTTP Basic auth in development

### As Alice (Buyer, EMEA)
GET http://localhost:4004/orders/Orders
Authorization: Basic alice:alice

### As Bob (Admin + Buyer, NA)
POST http://localhost:4004/orders/Orders
Authorization: Basic bob:bob
Content-Type: application/json

{ "title": "Test Order" }

### Unauthenticated
GET http://localhost:4004/orders/Orders
# No Authorization header — gets 401 if service requires authentication

cds.test with mock users

// test/orders.test.js
const cds = require('@sap/cds')

describe('Order Service', () => {
  // Set up test suite
  const alice = { user: 'alice', password: 'alice' }
  const bob   = { user: 'bob',   password: 'bob'   }

  const { GET, POST, DELETE } = cds.test('.').in(__dirname + '/..')

  it('Alice can read own orders', async () => {
    const { status, data } = await GET('/orders/Orders', { auth: alice })
    expect(status).toBe(200)
    expect(data.value.every(o => o.buyer_ID === 'alice')).toBe(true)
  })

  it('Alice cannot delete orders', async () => {
    const { status } = await DELETE('/orders/Orders(some-uuid)', { auth: alice })
    expect(status).toBe(403)
  })

  it('Bob can delete orders', async () => {
    const { status } = await DELETE('/orders/Orders(some-uuid)', { auth: bob })
    expect(status).toBe(204)
  })

  it('Unauthenticated users get 401', async () => {
    const { status } = await GET('/orders/Orders')
    expect(status).toBe(401)
  })
})

Testing multi-tenant scenarios

// .cdsrc.json — tenants in mock config
"carol-analyst": {
  "tenant": "tenant-B"    // different tenant from alice/bob
}
it('tenant B cannot see tenant A data', async () => {
  const carol = { user: 'carol-analyst', password: 'carol' }
  const { data } = await GET('/orders/Orders', { auth: carol })
  // Orders created by tenant-A users should not appear for tenant-B
  expect(data.value).toHaveLength(0)
})

Switching to real XSUAA (per-profile)

# Dev: use mocked auth
cds watch

# Staging: use real XSUAA
CDS_ENV=staging cds watch
# → picks up [staging] profile → "kind": "xsuaa"

Debugging auth in development

// Log every request's auth context
this.before('*', '*', (req) => {
  console.log({
    event:  req.event,
    target: req.target?.name,
    user:   req.user?.id,
    roles:  req.user?.roles,
    tenant: req.user?.tenant,
    attr:   req.user?.attr
  })
})
# Check auth headers in cds watch output
cds watch --verbose
# Enables request/response logging including auth

Edge cases to test

□ Unauthenticated request → 401
□ Authenticated, wrong role → 403
□ Authenticated, correct role, own data → 200
□ Authenticated, correct role, other user's data → filtered (not 403)
□ Admin role → sees all data
□ Service-to-service (no user) → check handling
□ Expired token simulation → not easy in dev, test on staging

Hands-on exercise

  1. Add 4 mock users: alice (Buyer), bob (Admin), carol (Analyst), david (no roles)
  2. Write integration tests covering all 4 users for your Orders service
  3. Add a test for multi-tenant isolation between alice and carol
  4. Add a BEFORE handler that logs the auth context — run tests and verify the log

Checkpoint ✓

Sign in for progress tracking & Q&A →