Klantportaal-tokens zijn de auth-grens tussen kantoor en klant; klanten loggen er op in om documentaanvragen af te handelen en de gebrande vragenlijst in te vullen. Tot mei 2026 bewaarde Taxmo de token in plaintext (legacy schema). Een gelekte database-dump zou dus direct werkende portaalcredentials bevatten. Per Wave-12 (mei 2026) bewaart Taxmo enkel de SHA-256 hex digest server-side; de plaintext token gaat één keer naar de klant per uitnodigingsmail en wordt daarna door de server vergeten.
client_portal_tokens.token is gedeprecateerd en mag NULL zijn. Een nieuwe kolom token_hash bevat de SHA-256 hex digest van de oorspronkelijke token. Een partial index idx_client_portal_tokens_hash (WHERE token_hash IS NOT NULL) ondersteunt de lookup. Bij nieuwe uitgiftes schrijft Taxmo NULL in token en de digest in token_hash; bij legacy rijen worden token + token_hash beide gevuld bij de eerste hit (lazy backfill) en token gewist.
De inkomende token wordt server-side gehasht (crypto.createHash('sha256').update(token).digest('hex')). Eerst hash-first lookup tegen idx_client_portal_tokens_hash. Geen match? Fallback naar plaintext-match op client_portal_tokens.token WITH client_portal_tokens.token_hash IS NULL - daarna wordt het gevonden plaintext-record gemigreerd (token_hash gevuld, token gewist) zodat het bij de volgende lookup direct hash-first matcht. Na 30 dagen kan een one-shot script de overgebleven plaintext-rijen NULL zetten en de fallback verwijderen.
256-bit random (crypto.randomBytes(32).toString('hex') - 64 hex chars). Geen salt nodig: een 256-bit random heeft genoeg entropie tegen brute-force. SHA-256 zonder salt is consistent met onze bestaande mobiele-token-hashing (auth.ts:hashToken). De token zelf wordt teruggegeven aan de aanroeper (het kantoor of de uitnodigingsmail) en daarna door de server vergeten. Verlies van de token-string betekent dat de klant een nieuwe moet aanvragen - er is geen recovery-pad op basis van de hash.
Alleen de SHA-256 digest. Tokens zijn 256-bit random; offline brute-force is computationeel onhaalbaar (2^256). Een aanvaller kan dus de portaaltoken niet reconstrueren uit de hash. Vergelijk met de oude situatie waarin een gelekte dump direct werkende portaalcredentials bevatte - dat is de hele reden voor de migratie.