Skip to content

Commit de0e456

Browse files
committed
feat: initial audit log setup, cleaned up duplicate tests
1 parent 30f2a5d commit de0e456

18 files changed

+1117
-1348
lines changed

FEATURE_SUGGESTIONS.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
- ✅ Salt for hashing
66
- ✅ Update last used timestamp
77
- ✅ Key tags/labels
8+
- ✅ Audit logging (opt-in)
89

910
## Recommended Additions
1011

@@ -125,8 +126,8 @@ const logs = await keys.getAuditLogs({
125126

126127
**High Priority:**
127128
1. Rate limiting (security)
128-
2. Update last used timestamp (already added)
129-
3. Audit logging (compliance)
129+
2. ~~Update last used timestamp~~ (already added)
130+
3. ~~Audit logging~~ ✅ (already added)
130131

131132
**Medium Priority:**
132133
4. Key rotation (security best practice)

README.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ A TypeScript library for secure API key management with cryptographic hashing, e
1414
- **Flexible Storage**: Memory, Redis, and Drizzle ORM adapters included
1515
- **Scope-based Permissions**: Fine-grained access control
1616
- **Key Management**: Enable/disable, rotate, and soft-revoke keys with audit trails
17+
- **Audit Logging**: Track who did what, when, and why (opt-in)
1718
- **TypeScript**: Full type safety
1819
- **Zero Config**: Works out of the box with sensible defaults
1920

@@ -80,6 +81,13 @@ const keys = createKeys({
8081
// Usage tracking
8182
autoTrackUsage: true, // Automatically update lastUsedAt on verify
8283

84+
// Audit logging (opt-in)
85+
auditLogs: true, // Enable audit logging
86+
auditContext: { // Default context for all audit logs (optional)
87+
userId: 'system',
88+
metadata: { service: 'api' }
89+
},
90+
8391
// Header detection
8492
headerNames: ['x-api-key', 'authorization'],
8593
extractBearer: true,
@@ -170,6 +178,78 @@ await keys.updateLastUsed(record.id)
170178
const result = await keys.verify(headers, { skipTracking: true })
171179
```
172180

181+
### Audit Logging
182+
183+
Track all key operations with context about who performed each action:
184+
185+
```typescript
186+
// Enable audit logging
187+
const keys = createKeys({
188+
auditLogs: true,
189+
auditContext: {
190+
// Default context merged into all logs
191+
metadata: { environment: 'production' }
192+
}
193+
})
194+
195+
// Actions are automatically logged with optional context
196+
await keys.create({
197+
ownerId: 'user_123',
198+
scopes: ['read']
199+
}, {
200+
userId: 'admin_456',
201+
ip: '192.168.1.1',
202+
metadata: { reason: 'New customer onboarding' }
203+
})
204+
205+
await keys.revoke('key_123', {
206+
userId: 'admin_789',
207+
metadata: { reason: 'Security breach' }
208+
})
209+
210+
// Query logs
211+
const logs = await keys.getLogs({
212+
keyId: 'key_123',
213+
action: 'revoked',
214+
startDate: '2025-01-01',
215+
limit: 100
216+
})
217+
218+
// Count logs
219+
const count = await keys.countLogs({ action: 'created' })
220+
221+
// Get statistics
222+
const stats = await keys.getLogStats('user_123')
223+
console.log(stats.total)
224+
console.log(stats.byAction.created)
225+
console.log(stats.lastActivity)
226+
227+
// Clean up old logs
228+
const deleted = await keys.deleteLogs({
229+
endDate: '2024-01-01'
230+
})
231+
232+
// Clear logs for a specific key
233+
await keys.clearLogs('key_123')
234+
```
235+
236+
**Log Entry Structure:**
237+
238+
```typescript
239+
{
240+
id: 'log_xyz',
241+
action: 'created' | 'revoked' | 'rotated' | 'enabled' | 'disabled',
242+
keyId: 'key_123',
243+
ownerId: 'user_456',
244+
timestamp: '2025-10-25T12:00:00.000Z',
245+
data: {
246+
userId: 'admin_789',
247+
ip: '192.168.1.1',
248+
metadata: { reason: 'Security breach' }
249+
}
250+
}
251+
```
252+
173253
### Helper Methods
174254

175255
```typescript

benchmark.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
/** biome-ignore-all lint/suspicious/noConsole: benchmarking */
2+
/** biome-ignore-all lint/style/noMagicNumbers: benchmarking */
3+
/** biome-ignore-all lint/suspicious/noExplicitAny: benchmarking */
14
import { bench, do_not_optimize, run, summary } from "mitata";
25
import { createKeys } from "./src/manager";
36
import { MemoryStore } from "./src/storage/memory";
@@ -23,8 +26,8 @@ for (let i = 0; i < 100; i++) {
2326
createdKeys.push(result);
2427
}
2528

26-
const testKey = createdKeys[0]!.key;
27-
const testRecord = createdKeys[0]!.record;
29+
const testKey = createdKeys[0]?.key;
30+
const testRecord = createdKeys[0]?.record;
2831
console.log("Setup complete\n");
2932

3033
async function main() {
@@ -49,6 +52,10 @@ async function main() {
4952

5053
await run();
5154

55+
if (!testKey) {
56+
throw new Error("Test key not found");
57+
}
58+
5259
// Verification Operations
5360
summary(() => {
5461
bench("verify() - valid key (fresh)", async () => {
@@ -182,7 +189,7 @@ async function main() {
182189
// Storage Layer
183190
summary(() => {
184191
bench("storage.save()", async () => {
185-
const { key, record } = await keys.create({
192+
const { record } = await keys.create({
186193
ownerId: "storage_user",
187194
name: "Storage Key",
188195
});

build.config.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
import { readFileSync } from "node:fs";
12
import { defineBuildConfig } from "unbuild";
23

4+
const pkg = JSON.parse(readFileSync("./package.json", "utf-8"));
5+
36
export default defineBuildConfig({
47
entries: [
58
"src/index.ts",
@@ -17,6 +20,15 @@ export default defineBuildConfig({
1720
minify: true,
1821
treeShaking: true,
1922
},
23+
output: {
24+
banner: `/*!
25+
* ${pkg.name} v${pkg.version}
26+
* ${pkg.description}
27+
* © ${new Date().getFullYear()} "Issa Nassar" <issa@databuddy.cc>
28+
* Released under the ${pkg.license} License
29+
* ${pkg.homepage}
30+
*/`,
31+
},
2032
},
2133
externals: [
2234
"drizzle-orm",

examples/databuddy-api.ts

Lines changed: 0 additions & 62 deletions
This file was deleted.

examples/drizzle-schema.ts

Lines changed: 0 additions & 36 deletions
This file was deleted.

0 commit comments

Comments
 (0)