@@ -252,15 +252,51 @@ export default class RoktManager {
252252 }
253253 }
254254
255- public hashAttributes ( attributes : IRoktPartnerAttributes ) : Promise < IRoktPartnerAttributes > {
256- if ( ! this . isReady ( ) ) {
257- return this . deferredCall < IRoktPartnerAttributes > ( 'hashAttributes' , attributes ) ;
258- }
259-
255+ /**
256+ * Hashes attributes and returns both original and hashed versions
257+ * with Rokt-compatible key names (like emailsha256, mobilesha256)
258+ *
259+ *
260+ * @param {IRoktPartnerAttributes } attributes - Attributes to hash
261+ * @returns {Promise<IRoktPartnerAttributes> } Object with both original and hashed attributes
262+ *
263+ */
264+ public async hashAttributes ( attributes : IRoktPartnerAttributes ) : Promise < IRoktPartnerAttributes > {
260265 try {
261- return this . kit . hashAttributes ( attributes ) ;
266+ if ( ! attributes || typeof attributes !== 'object' ) {
267+ return { } ;
268+ }
269+
270+ // Get own property keys only
271+ const keys = Object . keys ( attributes ) ;
272+
273+ if ( keys . length === 0 ) {
274+ return { } ;
275+ }
276+
277+ // Hash all attributes in parallel
278+ const hashPromises = keys . map ( async ( key ) => {
279+ const attributeValue = attributes [ key ] ;
280+ const hashedValue = await this . hashSha256 ( attributeValue ) ;
281+ return { key, attributeValue, hashedValue } ;
282+ } ) ;
283+
284+ const results = await Promise . all ( hashPromises ) ;
285+
286+ // Build the result object
287+ const hashedAttributes : IRoktPartnerAttributes = { } ;
288+ for ( const { key, attributeValue, hashedValue } of results ) {
289+ hashedAttributes [ key ] = attributeValue ;
290+ if ( hashedValue ) {
291+ hashedAttributes [ `${ key } sha256` ] = hashedValue ;
292+ }
293+ }
294+ return hashedAttributes ;
295+
262296 } catch ( error ) {
263- return Promise . reject ( error instanceof Error ? error : new Error ( 'Unknown error occurred' ) ) ;
297+ const errorMessage = error instanceof Error ? error . message : String ( error ) ;
298+ this . logger . error ( `Failed to hashAttributes, returning an empty object: ${ errorMessage } ` ) ;
299+ return { } ;
264300 }
265301 }
266302
@@ -290,6 +326,29 @@ export default class RoktManager {
290326 }
291327 }
292328
329+ /**
330+ * Hashes an attribute using SHA-256
331+ *
332+ * @param {string | number | boolean | undefined | null } attribute - The value to hash
333+ * @returns {Promise<string | undefined | null> } SHA-256 hashed value or undefined/null
334+ *
335+ */
336+ public async hashSha256 ( attribute : string | number | boolean | undefined | null ) : Promise < string | undefined | null > {
337+ if ( attribute === null || attribute === undefined ) {
338+ this . logger . warning ( `hashSha256 received null/undefined as input` ) ;
339+ return attribute as null | undefined ;
340+ }
341+
342+ try {
343+ const normalizedValue = String ( attribute ) . trim ( ) . toLocaleLowerCase ( ) ;
344+ return await this . sha256Hex ( normalizedValue ) ;
345+ } catch ( error ) {
346+ const errorMessage = error instanceof Error ? error . message : String ( error ) ;
347+ this . logger . error ( `Failed to hashSha256, returning undefined: ${ errorMessage } ` ) ;
348+ return undefined ;
349+ }
350+ }
351+
293352 public getLocalSessionAttributes ( ) : LocalSessionAttributes {
294353 return this . store . getLocalSessionAttributes ( ) ;
295354 }
@@ -400,6 +459,37 @@ export default class RoktManager {
400459 this . messageQueue . delete ( messageId ) ;
401460 }
402461
462+ /**
463+ * Hashes a string input using SHA-256 and returns the hex digest
464+ * Uses the Web Crypto API for secure hashing
465+ *
466+ * @param {string } input - The string to hash
467+ * @returns {Promise<string> } The SHA-256 hash as a hexadecimal string
468+ */
469+ private async sha256Hex ( input : string ) : Promise < string > {
470+ const encoder = new TextEncoder ( ) ;
471+ const encodedInput = encoder . encode ( input ) ;
472+ const digest = await crypto . subtle . digest ( 'SHA-256' , encodedInput ) ;
473+ return this . arrayBufferToHex ( digest ) ;
474+ }
475+
476+ /**
477+ * Converts an ArrayBuffer to a hexadecimal string representation
478+ * Each byte is converted to a 2-character hex string with leading zeros
479+ *
480+ * @param {ArrayBuffer } buffer - The buffer to convert
481+ * @returns {string } The hexadecimal string representation
482+ */
483+ private arrayBufferToHex ( buffer : ArrayBuffer ) : string {
484+ const bytes = new Uint8Array ( buffer ) ;
485+ let hexString = '' ;
486+ for ( let i = 0 ; i < bytes . length ; i ++ ) {
487+ const hexByte = bytes [ i ] . toString ( 16 ) . padStart ( 2 , '0' ) ;
488+ hexString += hexByte ;
489+ }
490+ return hexString ;
491+ }
492+
403493 /**
404494 * Checks if an identity value has changed by comparing current and new values
405495 *
0 commit comments