<div align="center">

![@djangocfg/crypto](https://raw.githubusercontent.com/markolofsen/assets/main/libs/djangocfg/crypto.webp)

</div>

# @djangocfg/crypto

Client-side AES-256-GCM decryption for Django-CFG encrypted API responses using Web Crypto API.

## Installation

```bash
pnpm add @djangocfg/crypto
```

## Features

- AES-256-GCM authenticated decryption
- PBKDF2 key derivation (matches Django-CFG backend)
- Zero dependencies (uses native Web Crypto API)
- TypeScript support with full type inference
- React hooks for declarative decryption
- Per-user and per-session key isolation

## Usage

### Basic Usage

```typescript
import { createDecryptionClient } from '@djangocfg/crypto';

// Create a decryption client
const crypto = await createDecryptionClient({
  secretKey: 'your-django-secret-key',
  userId: 123,  // optional, for per-user encryption
});

// Fetch encrypted data
const response = await fetch('/api/products/?encrypt=true');
const encryptedData = await response.json();

// Decrypt all encrypted fields
const data = await crypto.decryptObject(encryptedData);
console.log(data.price); // decrypted value
```

### React Hooks

```typescript
import { useDecrypt } from '@djangocfg/crypto/react';

function ProductPrice({ product }: { product: Product }) {
  const { data, isLoading, error } = useDecrypt(product, {
    secretKey: process.env.NEXT_PUBLIC_DECRYPT_KEY!,
    userId: user.id,
  });

  if (isLoading) return <Skeleton />;
  if (error) return <ErrorMessage error={error} />;

  return <span>${data.price}</span>;
}
```

### Lazy Decryption

```typescript
import { useLazyDecrypt } from '@djangocfg/crypto/react';

function LazyProduct({ product }: { product: Product }) {
  const { decrypt, data, isLoading } = useLazyDecrypt({
    secretKey: process.env.NEXT_PUBLIC_DECRYPT_KEY!,
  });

  return (
    <div>
      <button onClick={() => decrypt(product)}>
        Show Price
      </button>
      {isLoading && <Spinner />}
      {data && <span>{data.price}</span>}
    </div>
  );
}
```

## API Reference

### Core Functions

#### `createDecryptionClient(config)`

Creates a decryption client with pre-derived key.

```typescript
const crypto = await createDecryptionClient({
  secretKey: string;      // Django SECRET_KEY
  userId?: string|number; // Optional user ID
  sessionId?: string;     // Optional session ID
  iterations?: number;    // PBKDF2 iterations (default: 100000)
  keyPrefix?: string;     // Key prefix (default: "djangocfg_encryption")
});

// Methods
await crypto.decryptField(encryptedField);  // Decrypt single field
await crypto.decryptObject(data);           // Decrypt all fields recursively
crypto.isEncryptedField(value);             // Type guard
```

#### `decryptField(field, key)`

Decrypt a single encrypted field.

#### `decryptObject(data, key)`

Recursively decrypt all encrypted fields in an object.

### React Hooks

#### `useDecrypt(data, config)`

Decrypt data on mount.

#### `useDecryptionClient(config)`

Create a memoized decryption client.

#### `useLazyDecrypt(config)`

Decrypt data on demand with manual trigger.

#### `useIsEncrypted(value)`

Check if a value is encrypted.

## Types

```typescript
interface EncryptedField {
  encrypted: true;
  field?: string;
  algorithm: 'AES-256-GCM';
  iv: string;        // base64
  data: string;      // base64
  auth_tag: string;  // base64
}

interface DecryptionConfig {
  secretKey: string;
  userId?: string | number;
  sessionId?: string;
  iterations?: number;
  keyPrefix?: string;
}
```

## Security Notes

- Never expose your Django `SECRET_KEY` directly in frontend code
- Use a dedicated decryption key or derive one securely
- Consider per-user keys for sensitive data isolation
- PBKDF2 iterations must match backend configuration

## License

MIT
