@ideem/zsm-react-native
Version:
ZSM makes 2FA easy and invisible for everyone, all the time, using advanced cryptography like MPC to establish cryptographic proof of the origin of any transaction or login attempt, while eliminating opportunities for social engineering. ZSM has no relian
152 lines (143 loc) • 4.86 kB
JavaScript
import RelyingPartyBase from "./relying-party-base";
import AsyncStorage from "@react-native-async-storage/async-storage";
import ZSMLogger from "./zsm-logger";
/**
* Generate a proper UUID v4
* @returns {string} A valid UUID v4 string
*/
function generateUUID() {
// Use crypto.randomUUID if available, otherwise fallback
if (typeof crypto !== "undefined" && crypto.randomUUID) {
return crypto.randomUUID();
}
// Fallback for React Native
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
const r = (Math.random() * 16) | 0;
const v = c === "x" ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
}
/**
* React Native implementation of RelyingParty with AsyncStorage for identity mapping
* This extends the base class and provides platform-specific storage methods
*/
class RelyingParty extends RelyingPartyBase {
/**
* Get or create a storage UUID for a user.
* This UUID is used as consumer_id for MPC storage isolation on Android.
* Each user gets their own UUID so their MPC keys don't collide.
* @param {string} userId - The user identifier
* @returns {Promise<string>} The storage UUID (existing or newly generated)
*/
async getOrCreateStorageUuid(userId) {
try {
const key = `zsm_storage_uuid_${userId}`;
let uuid = await AsyncStorage.getItem(key);
if (uuid) {
ZSMLogger.debug(
`[STORAGE-UUID] Found existing UUID for ${userId}: ${uuid}`,
);
return uuid;
}
// Generate new UUID
uuid = generateUUID();
await AsyncStorage.setItem(key, uuid);
ZSMLogger.debug(
`[STORAGE-UUID] Generated new UUID for ${userId}: ${uuid}`,
);
return uuid;
} catch (error) {
ZSMLogger.error(
`[STORAGE-UUID] Failed to get/create UUID: ${error.message}`,
);
// Generate fallback UUID (won't be persisted, but allows enrollment to proceed)
return generateUUID();
}
}
/**
* Get the storage UUID for a user if it exists.
* @param {string} userId - The user identifier
* @returns {Promise<string|null>} The storage UUID or null if not found
*/
async getStorageUuid(userId) {
try {
const key = `zsm_storage_uuid_${userId}`;
const uuid = await AsyncStorage.getItem(key);
if (uuid) {
ZSMLogger.debug(`[STORAGE-UUID] Found UUID for ${userId}: ${uuid}`);
} else {
ZSMLogger.debug(`[STORAGE-UUID] No UUID found for ${userId}`);
}
return uuid;
} catch (error) {
ZSMLogger.error(`[STORAGE-UUID] Failed to get UUID: ${error.message}`);
return null;
}
}
/**
* React Native implementation of identity mapping storage using AsyncStorage
*/
async storeIdentityMapping(userId, identityId) {
try {
const key = `zsm_identity_mapping_${userId}`;
await AsyncStorage.setItem(key, identityId);
ZSMLogger.debug(
`[IDENTITY-MAPPING] Stored mapping: ${userId} -> ${identityId}`,
);
} catch (error) {
ZSMLogger.error(
`[IDENTITY-MAPPING] Failed to store mapping: ${error.message}`,
);
}
}
/**
* React Native implementation of identity mapping lookup using AsyncStorage
*/
async lookupIdentityMapping(userId) {
try {
const key = `zsm_identity_mapping_${userId}`;
ZSMLogger.debug(
`[IDENTITY-MAPPING] Looking up key: ${key} for user: ${userId}`,
);
// DEBUG: List all stored keys to see what's in AsyncStorage
try {
const allKeys = await AsyncStorage.getAllKeys();
const identityKeys = allKeys.filter((k) =>
k.startsWith("zsm_identity_mapping_"),
);
ZSMLogger.debug(
`[IDENTITY-MAPPING] All stored identity keys: ${JSON.stringify(identityKeys)}`,
);
for (const debugKey of identityKeys) {
const debugValue = await AsyncStorage.getItem(debugKey);
ZSMLogger.debug(`[IDENTITY-MAPPING] ${debugKey} = ${debugValue}`);
}
} catch (debugError) {
ZSMLogger.error(
`[IDENTITY-MAPPING] Debug listing failed: ${debugError.message}`,
);
}
const identityId = await AsyncStorage.getItem(key);
ZSMLogger.debug(
`[IDENTITY-MAPPING] AsyncStorage returned: ${identityId} for key: ${key}`,
);
if (identityId) {
ZSMLogger.debug(
`[IDENTITY-MAPPING] Found mapping: ${userId} -> ${identityId}`,
);
return identityId;
} else {
ZSMLogger.debug(
`[IDENTITY-MAPPING] No mapping found for ${userId}, returning original`,
);
return userId;
}
} catch (error) {
ZSMLogger.error(
`[IDENTITY-MAPPING] Failed to lookup mapping: ${error.message}`,
);
return userId;
}
}
}
export default RelyingParty;