How to obtain or force the SMS verification code (self-service 2-factor / email verification) in vuer_oss during dev testing, when the customer’s phone number is fake so no real SMS ever arrives. General FaceKom dev knowledge — not tied to one ticket.

TL;DR

Set test.security.tempTokenSms: "123456" in the box’s config/local.json, restart vuer_oss, then resend the code — it becomes 123456 for every send. Alternatively, read the customer’s current code directly via the Customer model getter customer.getVerificationCode() (it auto-decrypts; no manual crypto).

The Problem

Self-service NRT identification (e.g. InstaCash) sends a 2FA code over SMS. In dev the customer’s phone number is fake, so the SMS goes nowhere and you never see the code. You need a deterministic way to get past the verification step.

How the Code is Chosen

The send is driven by the customer:verification:sendSms ServiceBus hook in customization/listeners/sms-verification.js:

const tempSmsToken = config.get('test.security.tempTokenSms')
if (tempSmsToken) {
  verificationCode = tempSmsToken                 // fixed string, EVERY send
} else {
  verificationCode = customer.getVerificationCode()        // existing stored code
  if (!verificationCode || regenerate) {
    verificationCode = serviceContainer.service.contactValidation.generateToken()  // random
  }
}
customer.setVerificationCode(verificationCode)    // stores it AND
await customer.save()
// ...sends `verificationCode` in the SMS body

So:

  • If config.get('test.security.tempTokenSms') is truthy → that fixed string is used as the code for every send (both stored on the customer and put in the SMS).
  • Otherwise → the customer’s existing code is reused, or a fresh random 6-char token is generated via ContactValidationService.generateToken() (DI: serviceContainer.service.contactValidation).

Email verification works identically via test.security.tempTokenEmail in customization/listeners/e-mail-verification.js (the customer:verification:sendEmail hook).

For Agents

Config keys: test.security.tempTokenSms (SMS) and test.security.tempTokenEmail (email). Hooks: customer:verification:sendSms / customer:verification:sendEmail. Token generator: serviceContainer.service.contactValidation.generateToken(). Stored code field: customer videochatToken inside the encrypted customer.data TEXT column.

The Conventional Dev/Test Value: 123456

Every test/testconfigs/*.json sets:

"test": { "security": { "tempTokenEmail": "mailToken", "tempTokenSms": "123456" } }

So 123456 is the conventional fixed SMS code in tests, and mailToken the conventional fixed email code. (7 testconfigs carry the SMS block as of writing.)

Dev box does NOT have this set by default

The deployed dev box (lederera, NODE_ENV=dev) did not ship tempTokenSmsconfig.get('test.security.tempTokenSms') returned null. The testconfigs only apply when running the Jest/Playwright suites, not to a normal dev deployment. You must add it yourself.

Recipe A — Force a Fixed Code (preferred)

  1. Add the block to the box’s config/local.json (node-config: local.json overrides dev.json):
    { "test": { "security": { "tempTokenSms": "123456" } } }
  2. Restart vuer_oss. node-config caches config at process startup — the change is invisible until restart (e.g. supervisorctl restart vuer).
  3. Trigger a NEW send (resend code). The customer’s stored code only becomes 123456 on the next send. Entering 123456 against an already-sent (old random) code fails.
  4. Enter 123456.

Two gotchas that waste time

  • Restart required — editing local.json without restarting does nothing (config is cached at startup).
  • Resend required — the already-stored code is whatever random value was generated before you flipped the flag. The fixed value only takes effect on a fresh send.

Recipe B — Read the Customer’s Current Code

If you don’t want to restart, just read the code that was already generated and stored.

  • Source of truth: the customer’s videochatToken inside the customer.data column, exposed via customer.getVerificationCode().
    • customer.getVerificationCode() (server/db/model/customer.js:281-283) → PortalData.getVerificationCode() (customization/portal/PortalData.js:1704-1705) → getData('videochatToken').
  • customer.data is a TEXT column encrypted at rest (DataCryptoService, DI: serviceContainer.service.cryptos.data, keyed by the customer’s per-row encryption key). But the Sequelize model defines a get accessor on the data field (customer.js:23-51) that auto-decrypts on read (calls _getDecrypted at customer.js:316-318 when isEncrypted() i.e. !!this.key).
  • Net result: reading via the model returns plaintext — no manual decryption needed. Load the customer through the Sequelize model and call getVerificationCode().

Dead ends — don't waste time here

  • The smslogs table (messageBody, phoneNo) is separately encrypted and is not plaintext-readable.
  • SMS bodies are not written to the application log.
  • So the log files and raw DB ciphertext are dead ends for recovering the code. Use the model getter (or force a fixed token).

How Submitted Codes are Matched

ContactValidationService.matchTokens(expected, submitted) (server/service/ContactValidationService.js:16-20):

const regExp = new RegExp('^' + expectedToken + '$', 'i')
const token = submittedToken.replace(/^\s+|\s+$/g, '')   // trim
return regExp.test(token)

i.e. case-insensitive exact match (leading/trailing whitespace trimmed). 123456 and a random alphanumeric token both match cleanly. The generated token is 6 chars from a reduced alphabet (ambiguous chars ijlouvw0182z3b6g9q stripped), so no regex-metachar surprises in practice.