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’sconfig/local.json, restart vuer_oss, then resend the code — it becomes123456for every send. Alternatively, read the customer’s current code directly via the Customer model gettercustomer.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 bodySo:
- 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) andtest.security.tempTokenEmail(email). Hooks:customer:verification:sendSms/customer:verification:sendEmail. Token generator:serviceContainer.service.contactValidation.generateToken(). Stored code field: customervideochatTokeninside the encryptedcustomer.dataTEXT 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 shiptempTokenSms—config.get('test.security.tempTokenSms')returnednull. The testconfigs only apply when running the Jest/Playwright suites, not to a normaldevdeployment. You must add it yourself.
Recipe A — Force a Fixed Code (preferred)
- Add the block to the box’s
config/local.json(node-config:local.jsonoverridesdev.json):{ "test": { "security": { "tempTokenSms": "123456" } } } - Restart vuer_oss. node-config caches config at process startup — the change is invisible until restart (e.g.
supervisorctl restart vuer). - Trigger a NEW send (resend code). The customer’s stored code only becomes
123456on the next send. Entering123456against an already-sent (old random) code fails. - Enter
123456.
Two gotchas that waste time
- Restart required — editing
local.jsonwithout 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
videochatTokeninside thecustomer.datacolumn, exposed viacustomer.getVerificationCode().customer.getVerificationCode()(server/db/model/customer.js:281-283) →PortalData.getVerificationCode()(customization/portal/PortalData.js:1704-1705) →getData('videochatToken').
customer.datais a TEXT column encrypted at rest (DataCryptoService, DI:serviceContainer.service.cryptos.data, keyed by the customer’s per-row encryptionkey). But the Sequelize model defines agetaccessor on thedatafield (customer.js:23-51) that auto-decrypts on read (calls_getDecryptedatcustomer.js:316-318whenisEncrypted()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
smslogstable (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.
Related
- vuer_oss — backend service that owns the verification flow
- InstaCash is the self-service flow this was discovered against (see ASSICASH-71)
- security-audit — customer-data encryption context
- database-schema —
customer.data/smslogscolumns - agent-context — config access conventions (
config.get('path', default))