npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@arnonsang/secure-local-storage

v2.0.0

Published

Zero-config encrypted localStorage replacement using device fingerprinting

Readme

@arnonsang/secure-local-storage

npm version npm downloads License: MIT TypeScript Test Coverage Bundle Size

Zero-config encrypted localStorage replacement using device fingerprinting for automatic key generation.

Features

  • 🔐 Zero Configuration: No setup required, works out of the box
  • 🔒 Device-Bound Encryption: Uses device fingerprinting for automatic key generation
  • 🔄 Backward Compatible: Seamlessly migrates from legacy encryption methods
  • 🚀 Drop-in Replacement: Same API as localStorage but encrypted
  • 🛡️ AES-GCM Encryption: Military-grade encryption with PBKDF2 key derivation
  • 🌐 TypeScript Support: Full TypeScript definitions included
  • 📱 Browser Only: Designed for client-side browser storage

Installation

npm install @arnonsang/secure-local-storage

Usage

Basic Usage (Default/Legacy - No Configuration Needed)

The simplest way to use the library - works exactly like v1.x with enhanced v2.0 security:

import secureLocalStorage from '@arnonsang/secure-local-storage';

// Save encrypted data
await secureLocalStorage.setItem('user-token', 'your-secret-token');

// Retrieve and decrypt data
const token = await secureLocalStorage.getItem('user-token');
console.log(token); // 'your-secret-token'

// Remove encrypted data
secureLocalStorage.removeItem('user-token');

// Clear all encrypted data
secureLocalStorage.clear();

// Check storage info
console.log('Storage length:', secureLocalStorage.length);
console.log('All keys:', secureLocalStorage.keys());

// Get device fingerprint (useful for debugging)
const fingerprint = await secureLocalStorage.getDeviceFingerprint();

✅ This default instance automatically gets v2.0 security enhancements:

  • Dynamic salt generation
  • Enhanced device fingerprinting
  • Secure fallback mechanisms
  • Automatic migration from v1.x data

Advanced Usage (Custom Configuration - v2.0+)

For applications requiring enhanced security or custom settings, create your own configured instance:

import { SecureLocalStorage } from '@arnonsang/secure-local-storage';

// Create a custom configured instance
const customStorage = new SecureLocalStorage({
  customSalt: 'your-unique-app-salt',        // Custom salt for key derivation
  additionalEntropy: 'user-specific-data',   // Extra entropy for fingerprinting
  keyPrefix: 'myapp_',                       // Custom prefix for localStorage keys
  ivSuffix: '_vector',                       // Custom suffix for IV keys
  iterations: 150000,                        // Higher PBKDF2 iterations
  enableLegacySupport: false,                // Disable legacy compatibility
  enableDeprecationWarnings: true           // Show deprecation warnings
});

// Use the custom instance exactly like the default one
await customStorage.setItem('secure-data', 'sensitive-information');
const value = await customStorage.getItem('secure-data');

// Custom instance has all the same methods
console.log('Custom storage length:', customStorage.length);
console.log('Custom storage keys:', customStorage.keys());

// Runtime configuration changes
customStorage.configure({
  additionalEntropy: 'updated-context-data'
});

// Key rotation for enhanced security
await customStorage.rotateKeys();

🔐 Benefits of Custom Configuration:

  • Enhanced Security: Custom salts and entropy for your specific application
  • Isolation: Separate storage instances for different security contexts
  • Flexibility: Adjust security parameters based on your requirements
  • Future-Proof: Easy to enhance security without changing your codebase

Choosing Between Default and Custom Configuration

| Feature | Default Instance (import default) | Custom Instance (new SecureLocalStorage) | |---------|-----------------------------------|-------------------------------------------| | Ease of Use | ✅ Simplest - zero configuration | ⚡ Requires configuration object | | Backward Compatibility | ✅ 100% compatible with v1.x | ✅ Fully compatible | | Security Level | ✅ Enhanced v2.0 security | 🔐 Maximum customizable security | | Custom Salts | ❌ Uses generated salt | ✅ Your own application-specific salt | | Multiple Instances | ❌ Single global instance | ✅ Multiple isolated instances | | Key Rotation | ✅ Available | ✅ Available | | Runtime Config | ❌ Fixed configuration | ✅ Runtime configuration changes |

💡 Recommendation:

  • Use Default for: Simple projects, quick prototypes, migrating from v1.x
  • Use Custom for: Production apps, high-security requirements, multi-tenant applications

Usage with React

Option 1: Default Instance (Simplest)

import { useEffect, useState } from 'react';
import secureLocalStorage from '@arnonsang/secure-local-storage';

function useSecureStorage<T>(key: string, defaultValue: T) {
  const [value, setValue] = useState<T>(defaultValue);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const loadValue = async () => {
      try {
        const stored = await secureLocalStorage.getItem(key);
        if (stored !== null) {
          setValue(JSON.parse(stored));
        }
      } catch (error) {
        console.error('Failed to load from secure storage:', error);
      } finally {
        setLoading(false);
      }
    };

    loadValue();
  }, [key]);

  const setStoredValue = async (newValue: T) => {
    try {
      setValue(newValue);
      await secureLocalStorage.setItem(key, JSON.stringify(newValue));
    } catch (error) {
      console.error('Failed to save to secure storage:', error);
    }
  };

  return [value, setStoredValue, loading] as const;
}

// Usage in component
function MyComponent() {
  const [userData, setUserData, loading] = useSecureStorage('user-data', null);

  if (loading) return <div>Loading...</div>;

  return (
    <div>
      <pre>{JSON.stringify(userData, null, 2)}</pre>
      <button onClick={() => setUserData({ name: 'John', age: 30 })}>
        Save User Data
      </button>
    </div>
  );
}

Option 2: Custom Configuration (Enhanced Security)

import { useEffect, useState, useContext, createContext } from 'react';
import { SecureLocalStorage } from '@arnonsang/secure-local-storage';

// Create a custom storage instance for your app
const appStorage = new SecureLocalStorage({
  customSalt: 'my-react-app-salt-2024',
  additionalEntropy: 'user-session-context',
  keyPrefix: 'myapp_',
  iterations: 200000 // Higher security
});

// Optional: Create React Context for storage
const StorageContext = createContext(appStorage);

function useCustomSecureStorage<T>(key: string, defaultValue: T) {
  const storage = useContext(StorageContext);
  const [value, setValue] = useState<T>(defaultValue);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const loadValue = async () => {
      try {
        const stored = await storage.getItem(key);
        if (stored !== null) {
          setValue(JSON.parse(stored));
        }
      } catch (error) {
        console.error('Failed to load from secure storage:', error);
      } finally {
        setLoading(false);
      }
    };

    loadValue();
  }, [key, storage]);

  const setStoredValue = async (newValue: T) => {
    try {
      setValue(newValue);
      await storage.setItem(key, JSON.stringify(newValue));
    } catch (error) {
      console.error('Failed to save to secure storage:', error);
    }
  };

  return [value, setStoredValue, loading] as const;
}

Advanced Configuration (v2.0+)

For enhanced security, you can configure the library with custom settings:

import { SecureLocalStorage } from '@arnonsang/secure-local-storage';

const storage = new SecureLocalStorage({
  customSalt: 'your-custom-salt',           // Custom salt for key derivation
  additionalEntropy: 'extra-entropy',      // Additional entropy for fingerprinting
  keyPrefix: 'myapp_',                     // Custom prefix for keys
  ivSuffix: '_vector',                     // Custom suffix for IV keys
  iterations: 150000,                      // PBKDF2 iterations (default: 100000)
  enableLegacySupport: true,               // Enable legacy compatibility (default: true)
  enableDeprecationWarnings: false        // Show deprecation warnings (default: true)
});

// Runtime configuration changes
storage.configure({
  additionalEntropy: 'updated-entropy'
});

// Key rotation for enhanced security
await storage.rotateKeys();

Security Best Practices

// For sensitive applications, consider:
const secureStorage = new SecureLocalStorage({
  customSalt: 'your-unique-application-salt',
  additionalEntropy: 'user-specific-data',
  iterations: 200000, // Higher iterations for more security
});

// Implement periodic key rotation
setInterval(async () => {
  await secureStorage.rotateKeys();
}, 24 * 60 * 60 * 1000); // Rotate daily

How It Works

Encryption Process

  1. Device Fingerprinting: Creates a unique hash based on:

    • Canvas fingerprint
    • Screen properties
    • Browser/navigator information
    • WebGL properties
    • Timezone information
  2. Key Derivation: Uses PBKDF2 with 100,000 iterations to derive encryption keys from device fingerprint

  3. AES-GCM Encryption: Encrypts data using AES-GCM with 256-bit keys and random IVs

  4. Storage: Stores encrypted data and IV separately in localStorage with prefixed keys

Security Features

  • No Stored Keys: Encryption keys are never stored, only derived from device characteristics
  • Device Binding: Data can only be decrypted on the same device it was encrypted on
  • Enhanced Salt Generation: Dynamic salt generation using cryptographically secure random values
  • Custom Entropy Support: Add your own entropy for enhanced security
  • Key Rotation: Rotate encryption keys while preserving data
  • Automatic Migration: Seamlessly upgrades from legacy encryption methods
  • IV Randomization: Each encryption uses a fresh random IV for maximum security
  • Configurable Security: Adjust iterations, salts, and other security parameters

API Reference

Methods

setItem(key: string, value: string): Promise<void>

Encrypts and stores a string value.

getItem(key: string): Promise<string | null>

Retrieves and decrypts a stored value. Returns null if not found or decryption fails.

removeItem(key: string): void

Removes an encrypted item and its associated IV.

clear(): void

Removes all encrypted items managed by secure-local-storage.

hasItem(key: string): Promise<boolean>

Checks if an encrypted item exists and can be decrypted.

keys(): Promise<string[]>

Returns all keys of encrypted items.

getDeviceFingerprint(): Promise<string>

Returns the current device fingerprint (useful for debugging).

configure(config: Partial<SecureStorageConfig>): void

Updates the storage configuration at runtime.

rotateKeys(): Promise<void>

Rotates encryption keys while preserving all stored data.

Properties

length: number

Returns the number of encrypted items stored.

key(index: number): string | null

Returns the key at the specified index.

Browser Compatibility

  • ✅ Chrome 60+
  • ✅ Firefox 55+
  • ✅ Safari 11+
  • ✅ Edge 79+

Requires crypto.subtle API support.

Security Considerations

  • Device-Bound: Data is tied to the device it was encrypted on
  • Re-authentication: Device changes may require users to re-authenticate
  • Client-Side Only: This is designed for client-side storage security
  • Salt Management: Use custom salts for production applications
  • Key Rotation: Implement periodic key rotation for enhanced security
  • Entropy Sources: Consider adding application-specific entropy
  • Legacy Support: Disable legacy support in new applications for better security
  • Not for Sensitive Data: Don't store highly sensitive data that requires server-side encryption

Migration from v1.x

The v2.0 upgrade is automatically backward compatible. Your existing code will continue to work without changes:

// v1.x code - still works in v2.x
import secureLocalStorage from '@arnonsang/secure-local-storage';
await secureLocalStorage.setItem('key', 'value');

To take advantage of new security features:

// v2.x - enhanced security
import { SecureLocalStorage } from '@arnonsang/secure-local-storage';

const storage = new SecureLocalStorage({
  customSalt: 'your-app-salt',
  additionalEntropy: 'user-context'
});

Migration from localStorage

// Before
localStorage.setItem('key', 'value');
const value = localStorage.getItem('key');

// After
await secureLocalStorage.setItem('key', 'value');
const value = await secureLocalStorage.getItem('key');

License

MIT © arnonsang

Contributing

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request