Skip to content

Commit b3866f3

Browse files
committed
Refactor README: bilingual (EN/IT)
1 parent 032ee3f commit b3866f3

1 file changed

Lines changed: 88 additions & 244 deletions

File tree

README.md

Lines changed: 88 additions & 244 deletions
Original file line numberDiff line numberDiff line change
@@ -1,309 +1,153 @@
1-
# fastify-api-key
1+
<h1 align="center">@fracabu/fastify-api-key</h1>
2+
<h3 align="center">Complete API Key authentication for Fastify</h3>
23

3-
[![npm version](https://img.shields.io/npm/v/fastify-api-key.svg)](https://www.npmjs.com/package/fastify-api-key)
4-
[![CI](https://github.com/fracabu/fastify-api-key/actions/workflows/ci.yml/badge.svg)](https://github.com/fracabu/fastify-api-key/actions/workflows/ci.yml)
5-
[![codecov](https://codecov.io/gh/fracabu/fastify-api-key/branch/main/graph/badge.svg)](https://codecov.io/gh/fracabu/fastify-api-key)
6-
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4+
<p align="center">
5+
<em>Scopes, multiple sources, and TypeScript support</em>
6+
</p>
77

8-
Complete API Key authentication for Fastify with scopes, multiple sources, and TypeScript support.
8+
<p align="center">
9+
<a href="https://www.npmjs.com/package/@fracabu/fastify-api-key"><img src="https://img.shields.io/npm/v/@fracabu/fastify-api-key.svg" alt="npm version" /></a>
10+
<img src="https://github.com/fracabu/fastify-api-key/actions/workflows/ci.yml/badge.svg" alt="CI" />
11+
<img src="https://img.shields.io/badge/Fastify-5.x-000000?style=flat-square&logo=fastify" alt="Fastify" />
12+
<img src="https://img.shields.io/badge/TypeScript-Ready-blue.svg" alt="TypeScript" />
13+
</p>
914

10-
## Features
15+
<p align="center">
16+
:gb: <a href="#english">English</a> | :it: <a href="#italiano">Italiano</a>
17+
</p>
18+
19+
---
20+
21+
## Overview
22+
23+
<!-- ![fastify-api-key Overview](assets/apikey-overview.png) -->
24+
25+
---
26+
27+
<a name="english"></a>
28+
## :gb: English
29+
30+
### Features
1131

1232
- **Fastify v5** support
1333
- **TypeScript-first** with complete type definitions
1434
- **Multiple extraction sources** (header, query, body, cookie)
15-
- **Scopes/permissions system** with `scopes` (all required) and `anyScope` (at least one)
35+
- **Scopes/permissions system**
1636
- **Rate limiting** information support
1737
- **Timing-safe** key comparison (prevents timing attacks)
1838
- **Custom error handlers**
19-
- **Validation hooks** for logging/audit
2039
- **ESM and CJS** dual module support
2140

22-
## Installation
41+
### Install
2342

2443
```bash
2544
npm install @fracabu/fastify-api-key
2645
```
2746

28-
## Requirements
29-
30-
- Node.js >= 20.0.0
31-
- Fastify >= 5.0.0
32-
33-
## Quick Start
47+
### Quick Start
3448

3549
```typescript
36-
import Fastify from 'fastify';
37-
import fastifyApiKey from '@fracabu/fastify-api-key';
50+
import Fastify from 'fastify'
51+
import fastifyApiKey from '@fracabu/fastify-api-key'
3852

39-
const app = Fastify();
53+
const app = Fastify()
4054

41-
// Register the plugin with a validation function
4255
await app.register(fastifyApiKey, {
4356
validate: async (key) => {
44-
// Your validation logic (database lookup, etc.)
45-
const apiKey = await db.apiKeys.findByKey(key);
46-
47-
if (!apiKey) {
48-
return { valid: false };
49-
}
50-
51-
return {
52-
valid: true,
53-
scopes: apiKey.scopes,
54-
metadata: { userId: apiKey.userId }
55-
};
57+
const apiKey = await db.apiKeys.findByKey(key)
58+
if (!apiKey) return { valid: false }
59+
return { valid: true, scopes: apiKey.scopes }
5660
}
57-
});
61+
})
5862

5963
// Protected route
6064
app.get('/api/users', {
6165
preHandler: app.apiKey()
6266
}, async (request) => {
63-
console.log('Scopes:', request.apiKeyScopes);
64-
console.log('Metadata:', request.apiKey?.metadata);
65-
return { users: [] };
66-
});
67+
return { users: [] }
68+
})
6769

68-
// Route with required scopes (all must be present)
70+
// Route with required scopes
6971
app.delete('/api/users/:id', {
7072
preHandler: app.apiKey({ scopes: ['admin', 'users:delete'] })
71-
}, async () => {
72-
return { deleted: true };
73-
});
74-
75-
// Route with anyScope (at least one must be present)
76-
app.get('/api/reports', {
77-
preHandler: app.apiKey({ anyScope: ['reports:read', 'admin'] })
78-
}, async () => {
79-
return { reports: [] };
80-
});
81-
82-
await app.listen({ port: 3000 });
73+
}, handler)
8374
```
8475

85-
## API Reference
86-
87-
### Plugin Options
88-
89-
| Option | Type | Default | Description |
90-
|--------|------|---------|-------------|
91-
| `validate` | `ApiKeyValidator` | **required** | Validation function |
92-
| `sources` | `ApiKeySource[]` | `[{ type: 'header', name: 'X-API-Key' }]` | Key extraction sources |
93-
| `errorHandler` | `ApiKeyErrorHandler` | `undefined` | Custom error handler |
94-
| `decoratorName` | `string` | `'apiKey'` | Request decorator name |
95-
| `allowAnonymous` | `boolean` | `false` | Allow unauthenticated requests |
96-
| `onValidation` | `ApiKeyHook` | `undefined` | Post-validation hook |
97-
| `timingSafe` | `boolean` | `true` | Use timing-safe comparison |
98-
99-
### Validation Function
100-
101-
The `validate` function receives the API key and request, and should return a validation result:
76+
### Utilities
10277

10378
```typescript
104-
interface ApiKeyValidationResult {
105-
valid: boolean;
106-
scopes?: string[];
107-
rateLimit?: {
108-
limit: number;
109-
remaining: number;
110-
reset: number;
111-
};
112-
metadata?: Record<string, unknown>;
113-
errorMessage?: string;
114-
}
115-
```
116-
117-
### Guard Options
79+
import { generateApiKey, timingSafeCompare } from '@fracabu/fastify-api-key'
11880

119-
```typescript
120-
app.apiKey({
121-
scopes: ['read', 'write'], // All required
122-
anyScope: ['admin', 'superuser'], // At least one required
123-
allowAnonymous: false
124-
})
81+
const key = generateApiKey({ prefix: 'myapp', length: 32 })
82+
// => 'myapp_aBcDeFgHiJkLmNoPqRsTuVwXyZ012345'
12583
```
12684

127-
## Examples
85+
---
12886

129-
### Multiple Sources
87+
<a name="italiano"></a>
88+
## :it: Italiano
13089

131-
Extract API key from multiple locations with priority:
90+
### Funzionalita
13291

133-
```typescript
134-
await app.register(fastifyApiKey, {
135-
sources: [
136-
{ type: 'header', name: 'X-API-Key' },
137-
{ type: 'header', name: 'Authorization', prefix: 'ApiKey ' },
138-
{ type: 'query', name: 'api_key' }
139-
],
140-
validate: async (key) => {
141-
// ...
142-
}
143-
});
144-
```
145-
146-
### Rate Limiting Information
92+
- Supporto **Fastify v5**
93+
- **TypeScript-first** con definizioni di tipo complete
94+
- **Sorgenti di estrazione multiple** (header, query, body, cookie)
95+
- **Sistema scopes/permessi**
96+
- Supporto informazioni **rate limiting**
97+
- Confronto chiavi **timing-safe** (previene timing attacks)
98+
- **Error handler personalizzati**
99+
- Supporto modulo duale **ESM e CJS**
147100

148-
Return rate limit info from your validator:
101+
### Installazione
149102

150-
```typescript
151-
await app.register(fastifyApiKey, {
152-
validate: async (key) => {
153-
const keyData = await db.apiKeys.findByKey(key);
154-
const usage = await rateLimiter.getUsage(key);
155-
156-
return {
157-
valid: true,
158-
scopes: keyData.scopes,
159-
rateLimit: {
160-
limit: keyData.rateLimit,
161-
remaining: keyData.rateLimit - usage.count,
162-
reset: usage.resetAt
163-
}
164-
};
165-
}
166-
});
167-
168-
// Add rate limit headers
169-
app.addHook('onSend', (request, reply, _payload, done) => {
170-
if (request.apiKey?.rateLimit) {
171-
const { limit, remaining, reset } = request.apiKey.rateLimit;
172-
reply.header('X-RateLimit-Limit', limit);
173-
reply.header('X-RateLimit-Remaining', remaining);
174-
reply.header('X-RateLimit-Reset', reset);
175-
}
176-
done();
177-
});
103+
```bash
104+
npm install @fracabu/fastify-api-key
178105
```
179106

180-
### Custom Error Handler
107+
### Quick Start
181108

182109
```typescript
183-
await app.register(fastifyApiKey, {
184-
validate: async (key) => { /* ... */ },
185-
errorHandler: async (error, request, reply) => {
186-
request.log.warn({ err: error }, 'API key validation failed');
187-
188-
await reply.status(error.statusCode).send({
189-
error: error.code,
190-
message: error.message,
191-
timestamp: new Date().toISOString()
192-
});
193-
}
194-
});
195-
```
110+
import Fastify from 'fastify'
111+
import fastifyApiKey from '@fracabu/fastify-api-key'
196112

197-
### Validation Hook for Audit Logging
113+
const app = Fastify()
198114

199-
```typescript
200115
await app.register(fastifyApiKey, {
201-
validate: async (key) => { /* ... */ },
202-
onValidation: async (key, result, request) => {
203-
await auditLog.record({
204-
timestamp: new Date(),
205-
ip: request.ip,
206-
path: request.url,
207-
method: request.method,
208-
apiKeyPrefix: key.substring(0, 10) + '...',
209-
success: result.valid,
210-
scopes: result.scopes
211-
});
116+
validate: async (key) => {
117+
const apiKey = await db.apiKeys.findByKey(key)
118+
if (!apiKey) return { valid: false }
119+
return { valid: true, scopes: apiKey.scopes }
212120
}
213-
});
214-
```
215-
216-
### Optional Authentication
217-
218-
```typescript
219-
// Global anonymous access
220-
await app.register(fastifyApiKey, {
221-
allowAnonymous: true,
222-
validate: async (key) => { /* ... */ }
223-
});
121+
})
224122

225-
// Route works with or without API key
226-
app.get('/api/posts', {
123+
// Rotta protetta
124+
app.get('/api/users', {
227125
preHandler: app.apiKey()
228126
}, async (request) => {
229-
if (request.apiKey) {
230-
// Authenticated - show all posts
231-
return { posts: await getAllPosts() };
232-
}
233-
// Anonymous - show only public posts
234-
return { posts: await getPublicPosts() };
235-
});
236-
237-
// This route still requires authentication
238-
app.post('/api/posts', {
239-
preHandler: app.apiKey({ allowAnonymous: false })
240-
}, async () => {
241-
return { created: true };
242-
});
243-
```
244-
245-
## Exported Utilities
246-
247-
The package also exports utility functions:
248-
249-
```typescript
250-
import {
251-
generateApiKey,
252-
timingSafeCompare,
253-
hasAllScopes,
254-
hasAnyScope
255-
} from '@fracabu/fastify-api-key';
256-
257-
// Generate a secure API key
258-
const key = generateApiKey({ prefix: 'myapp', length: 32 });
259-
// => 'myapp_aBcDeFgHiJkLmNoPqRsTuVwXyZ012345'
260-
261-
// Timing-safe comparison
262-
const isValid = timingSafeCompare(providedKey, storedKey);
263-
264-
// Scope helpers
265-
hasAllScopes(['read', 'write', 'admin'], ['read', 'write']); // true
266-
hasAnyScope(['read'], ['admin', 'read']); // true
267-
```
268-
269-
## Error Classes
127+
return { users: [] }
128+
})
270129

271-
```typescript
272-
import {
273-
ApiKeyError,
274-
MissingApiKeyError,
275-
InvalidApiKeyError,
276-
InsufficientScopesError,
277-
RateLimitExceededError
278-
} from '@fracabu/fastify-api-key';
279-
280-
// All errors have: code, message, statusCode, toJSON()
130+
// Rotta con scopes richiesti
131+
app.delete('/api/users/:id', {
132+
preHandler: app.apiKey({ scopes: ['admin', 'users:delete'] })
133+
}, handler)
281134
```
282135

283-
## TypeScript Support
136+
---
284137

285-
The plugin provides full TypeScript support with Fastify module augmentation:
138+
## Requirements
286139

287-
```typescript
288-
import type {
289-
FastifyApiKeyOptions,
290-
ApiKeySource,
291-
ApiKeyValidationResult,
292-
ApiKeyValidator,
293-
ApiKeyErrorHandler,
294-
ApiKeyHook,
295-
ApiKeyGuardOptions,
296-
ApiKeyData
297-
} from '@fracabu/fastify-api-key';
298-
299-
// request.apiKey and request.apiKeyScopes are properly typed
300-
app.get('/test', { preHandler: app.apiKey() }, async (request) => {
301-
const scopes = request.apiKeyScopes; // string[] | undefined
302-
const metadata = request.apiKey?.metadata; // Record<string, unknown>
303-
});
304-
```
140+
- Node.js >= 20.0.0
141+
- Fastify >= 5.0.0
305142

306143
## License
307144

308145
MIT
309146

147+
---
148+
149+
<p align="center">
150+
<a href="https://github.com/fracabu">
151+
<img src="https://img.shields.io/badge/Made_by-fracabu-8B5CF6?style=flat-square" alt="Made by fracabu" />
152+
</a>
153+
</p>

0 commit comments

Comments
 (0)