UNPKG

@aparajita/capacitor-secure-storage

Version:

Capacitor 7+ plugin that provides secure storage for the iOS and Android

162 lines (161 loc) 5.86 kB
/* eslint-disable @typescript-eslint/require-await */ import { Capacitor, CapacitorException, WebPlugin } from '@capacitor/core'; import { KeychainAccess, StorageError, StorageErrorType, } from './definitions.js'; function isStorageErrorType(value) { return value !== undefined && Object.keys(StorageErrorType).includes(value); } export class SecureStorageBase extends WebPlugin { constructor() { super(...arguments); this.prefix = 'capacitor-storage_'; this.sync = false; this.access = KeychainAccess.whenUnlocked; } async setSynchronize(sync) { this.sync = sync; if (Capacitor.getPlatform() === 'ios') { return this.setSynchronizeKeychain({ sync }); } // no-op on other platforms } async getSynchronize() { return this.sync; } async setDefaultKeychainAccess(access) { this.access = access; } async tryOperation(operation) { try { // Ensure that only one operation is in progress at a time. return await operation(); } catch (error) { // Native calls which reject will throw a CapacitorException with a code. // We want to convert these to StorageErrors. if (error instanceof CapacitorException && isStorageErrorType(error.code)) { throw new StorageError(error.message, error.code); } throw error; } } async get(key, convertDate = true, sync) { if (key) { const { data } = await this.tryOperation(async () => this.internalGetItem({ prefixedKey: this.prefixedKey(key), sync: sync !== null && sync !== void 0 ? sync : this.sync, })); if (data === null) { return null; } if (convertDate) { const date = parseISODate(data); if (date) { return date; } } try { // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion return JSON.parse(data); } catch (_a) { throw new StorageError('Invalid data', StorageErrorType.invalidData); } } return SecureStorageBase.missingKey(); } async getItem(key) { if (key) { const { data } = await this.tryOperation(async () => this.internalGetItem({ prefixedKey: this.prefixedKey(key), sync: this.sync, })); return data; } return null; } async set(key, data, convertDate = true, sync, access) { if (key) { let convertedData = data; if (convertDate && data instanceof Date) { convertedData = data.toISOString(); } return this.tryOperation(async () => this.internalSetItem({ prefixedKey: this.prefixedKey(key), data: JSON.stringify(convertedData), sync: sync !== null && sync !== void 0 ? sync : this.sync, access: access !== null && access !== void 0 ? access : this.access, })); } return SecureStorageBase.missingKey(); } async setItem(key, value) { if (key) { return this.tryOperation(async () => this.internalSetItem({ prefixedKey: this.prefixedKey(key), data: value, sync: this.sync, access: this.access, })); } return SecureStorageBase.missingKey(); } async remove(key, sync) { if (key) { const { success } = await this.tryOperation(async () => this.internalRemoveItem({ prefixedKey: this.prefixedKey(key), sync: sync !== null && sync !== void 0 ? sync : this.sync, })); return success; } return SecureStorageBase.missingKey(); } async removeItem(key) { if (key) { await this.tryOperation(async () => this.internalRemoveItem({ prefixedKey: this.prefixedKey(key), sync: this.sync, })); return; } SecureStorageBase.missingKey(); } async keys(sync) { const { keys } = await this.tryOperation(async () => this.getPrefixedKeys({ prefix: this.prefix, sync: sync !== null && sync !== void 0 ? sync : this.sync, })); const prefixLength = this.prefix.length; return keys.map((key) => key.slice(prefixLength)); } async getKeyPrefix() { return this.prefix; } async setKeyPrefix(prefix) { this.prefix = prefix; } prefixedKey(key) { return this.prefix + key; } static missingKey() { throw new StorageError('No key provided', StorageErrorType.missingKey); } } // RegExp to match an ISO 8601 date string in the form YYYY-MM-DDTHH:mm:ss.sssZ const isoDateRE = /^"(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}).(\d{3})Z"$/u; function parseISODate(isoDate) { const match = isoDateRE.exec(isoDate); if (match) { const year = Number.parseInt(match[1], 10); const month = Number.parseInt(match[2], 10) - 1; // month is zero-based const day = Number.parseInt(match[3], 10); const hour = Number.parseInt(match[4], 10); const minute = Number.parseInt(match[5], 10); const second = Number.parseInt(match[6], 10); const millis = Number.parseInt(match[7], 10); const epochTime = Date.UTC(year, month, day, hour, minute, second, millis); return new Date(epochTime); } return null; } /* eslint-enable @typescript-eslint/require-await */