@darkstrata/credential-check
v1.3.0
Published
Check if credentials have been exposed in data breaches using k-anonymity
Readme
@darkstrata/credential-check
Check if credentials have been exposed in data breaches using k-anonymity.
Features
- Privacy-first: Uses k-anonymity to check credentials without exposing them
- Type-safe: Full TypeScript support with comprehensive type definitions
- Zero dependencies: Uses Node.js built-in
cryptoandfetch - Automatic caching: Intelligent caching aligned with server time windows
- Batch processing: Efficiently check multiple credentials with automatic prefix grouping
- Retry logic: Built-in exponential backoff for transient failures
- Comprehensive errors: Detailed error types for easy error handling
Prerequisites
- Get an API key from https://darkstrata.io
- Node.js 18.0.0 or higher
Installation
npm install @darkstrata/credential-checkyarn add @darkstrata/credential-checkpnpm add @darkstrata/credential-checkQuick Start
import { DarkStrataCredentialCheck } from '@darkstrata/credential-check';
// Create a client
const client = new DarkStrataCredentialCheck({
apiKey: 'your-api-key',
});
// Check a single credential
const result = await client.check('[email protected]', 'password123');
if (result.found) {
console.log('⚠️ This credential was found in a data breach!');
} else {
console.log('✓ Credential not found in known breaches.');
}How It Works
This SDK uses k-anonymity to check credentials without exposing them:
- Your credential is hashed locally:
SHA256(email:password) - Only the first 5 characters (prefix) of the hash are sent to the API
- The API returns all hashes matching that prefix (1-in-1,000,000 anonymity)
- The SDK checks if your full hash is in the returned set
Your actual credentials never leave your system.
┌─────────────────────┐ ┌─────────────────────┐
│ Your System │ │ DarkStrata API │
│ │ │ │
│ email:password │ │ │
│ ↓ │ │ │
│ SHA256 hash │ │ │
│ ↓ │ │ │
│ Extract prefix ────┼────────→│ Lookup by prefix │
│ (5 chars only) │ │ ↓ │
│ │←────────┼─ Return all matches │
│ Check membership │ │ │
│ ↓ │ │ │
│ found: true/false │ │ │
└─────────────────────┘ └─────────────────────┘API Reference
DarkStrataCredentialCheck
The main client class.
Constructor
new DarkStrataCredentialCheck(options: ClientOptions)Options:
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| apiKey | string | required | Your DarkStrata API key |
| baseUrl | string | 'https://api.darkstrata.io/v1/' | API base URL |
| timeout | number | 30000 | Request timeout in milliseconds |
| retries | number | 3 | Number of retry attempts |
| enableCaching | boolean | true | Enable response caching |
| cacheTTL | number | 3600000 | Cache TTL in milliseconds (1 hour) |
Methods
check(email, password)
Check a single credential.
const result = await client.check('[email protected]', 'password123');Returns: Promise<CheckResult>
checkHash(hash)
Check a pre-computed SHA-256 hash.
const hash = '5BAA61E4C9B93F3F0682250B6CF8331B7EE68FD8...';
const result = await client.checkHash(hash);Returns: Promise<CheckResult>
checkBatch(credentials)
Check multiple credentials efficiently.
const results = await client.checkBatch([
{ email: '[email protected]', password: 'pass1' },
{ email: '[email protected]', password: 'pass2' },
]);Returns: Promise<CheckResult[]>
clearCache()
Clear the internal response cache.
client.clearCache();getCacheSize()
Get the number of cached entries.
const size = client.getCacheSize();CheckResult
The result of a credential check.
interface CheckResult {
found: boolean; // true if credential was in a breach
credential: {
email: string; // The email that was checked
masked: true; // Password is always masked
};
metadata: {
prefix: string; // The 5-char prefix used
totalResults: number; // Total hashes returned by API
hmacSource: 'server' | 'client'; // Source of HMAC key
timeWindow?: number; // Server time window (server HMAC only)
filterSince?: number; // Epoch day filter (if since was used)
cachedResult: boolean; // Whether result was from cache
checkedAt: Date; // When the check was performed
};
}CheckOptions
Optional parameters for check requests.
interface CheckOptions {
clientHmac?: string; // Your own HMAC key (64+ hex chars)
since?: number | Date; // Filter by breach date
}Error Handling
The SDK provides specific error types for different failure scenarios:
import {
DarkStrataCredentialCheck,
AuthenticationError,
ValidationError,
ApiError,
TimeoutError,
NetworkError,
RateLimitError,
isDarkStrataError,
} from '@darkstrata/credential-check';
const client = new DarkStrataCredentialCheck({ apiKey: 'your-key' });
try {
const result = await client.check('[email protected]', 'password');
} catch (error) {
if (error instanceof AuthenticationError) {
console.error('Invalid API key');
} else if (error instanceof ValidationError) {
console.error(`Invalid input: ${error.field}`);
} else if (error instanceof RateLimitError) {
console.error(`Rate limited. Retry after ${error.retryAfter} seconds`);
} else if (error instanceof TimeoutError) {
console.error(`Request timed out after ${error.timeoutMs}ms`);
} else if (error instanceof NetworkError) {
console.error('Network error:', error.message);
} else if (error instanceof ApiError) {
console.error(`API error ${error.statusCode}:`, error.message);
} else if (isDarkStrataError(error)) {
console.error(`DarkStrata error [${error.code}]:`, error.message);
} else {
throw error;
}
}Error Types
| Error | Code | Description |
|-------|------|-------------|
| AuthenticationError | AUTHENTICATION_ERROR | Invalid or missing API key |
| ValidationError | VALIDATION_ERROR | Invalid input parameters |
| ApiError | API_ERROR | API request failed |
| TimeoutError | TIMEOUT_ERROR | Request timed out |
| NetworkError | NETWORK_ERROR | Network connectivity issue |
| RateLimitError | RATE_LIMIT_ERROR | Rate limit exceeded |
Advanced Usage
Pre-computed Hashes
If you're storing hashed credentials, you can check them directly:
import { hashCredential, DarkStrataCredentialCheck } from '@darkstrata/credential-check';
// Compute hash once
const hash = hashCredential('[email protected]', 'password123');
// Store hash securely...
// Later, check the hash
const client = new DarkStrataCredentialCheck({ apiKey: 'your-key' });
const result = await client.checkHash(hash);Batch Processing
For checking multiple credentials, use checkBatch for efficiency:
const credentials = [
{ email: '[email protected]', password: 'pass1' },
{ email: '[email protected]', password: 'pass2' },
{ email: '[email protected]', password: 'pass3' },
];
const results = await client.checkBatch(credentials);
const compromised = results.filter(r => r.found);
console.log(`${compromised.length} credentials were compromised`);Batch processing automatically groups credentials by prefix to minimise API calls.
Client-Provided HMAC Key
By default, the server generates a time-rotating HMAC key. For deterministic results across requests, provide your own key:
import { randomBytes } from 'node:crypto';
// Generate a secure key once and store it securely
const clientHmac = randomBytes(32).toString('hex');
const result = await client.check('[email protected]', 'password', {
clientHmac,
});
// Results are now deterministic (not time-windowed)
console.log(result.metadata.hmacSource); // 'client'When to use client HMAC:
- You need consistent results across multiple requests
- You're comparing results from different time periods
- You want to avoid server-side key rotation
Date Filtering
Filter results to only include breaches from a specific date onwards:
// Only check breaches from 2024 onwards
const result = await client.check('[email protected]', 'password', {
since: new Date('2024-01-01'),
});
// Or use epoch day (days since 1 January 1970)
const result = await client.check('[email protected]', 'password', {
since: 19724, // 2024-01-01
});
// Check the filter applied
console.log(result.metadata.filterSince); // 19724Combined Options
You can combine multiple options:
const result = await client.check('[email protected]', 'password', {
clientHmac: 'your-256-bit-hex-key...',
since: new Date('2024-01-01'),
});Disabling Cache
For real-time checks where you need fresh results:
const client = new DarkStrataCredentialCheck({
apiKey: 'your-key',
enableCaching: false,
});Custom Timeout and Retries
const client = new DarkStrataCredentialCheck({
apiKey: 'your-key',
timeout: 60000, // 60 seconds
retries: 5, // 5 retry attempts
});Security Considerations
What is sent to the API?
- Only the first 5 characters of the SHA-256 hash
- Your API key for authentication
What is NOT sent?
- Your email address
- Your password
- The full hash of your credentials
Best Practices
- Never log credentials - The SDK never logs credentials, and you shouldn't either
- Use HTTPS - The SDK enforces HTTPS for all API calls
- Secure your API key - Store your API key securely (environment variables, secrets manager)
- Handle errors gracefully - Don't expose internal errors to end users
TypeScript
This package is written in TypeScript and includes full type definitions.
import type {
ClientOptions,
Credential,
CheckResult,
CheckMetadata,
} from '@darkstrata/credential-check';CommonJS Support
The package supports both ESM and CommonJS:
// ESM
import { DarkStrataCredentialCheck } from '@darkstrata/credential-check';
// CommonJS
const { DarkStrataCredentialCheck } = require('@darkstrata/credential-check');Contributing
See the contributing guide.
Licence
Apache 2.0 © DarkStrata
