@a_jackie_z/fastify 1.1.7 → 1.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +89 -66
- package/dist/index.d.ts +20 -1
- package/dist/index.js +49 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -43,6 +43,8 @@ await runFastify(app, '0.0.0.0', 3000)
|
|
|
43
43
|
## Features
|
|
44
44
|
|
|
45
45
|
- **JWT Authentication** - Built-in JWT support with global and route-level configuration
|
|
46
|
+
- **Service JWT Tokens** - Service-to-service authentication with `generateServiceToken()` for microservice communication
|
|
47
|
+
- **Dual JWT Support** - User JWT (client identity) and Service JWT (gateway authentication) in the same request
|
|
46
48
|
- **Rate Limiting** - Global rate limiting for API protection
|
|
47
49
|
- **Swagger Documentation** - Auto-generated API documentation with Zod schemas and bearer authentication
|
|
48
50
|
- **Zod Integration** - Type-safe request/response validation
|
|
@@ -519,6 +521,7 @@ interface JWTRouteConfig {
|
|
|
519
521
|
| boolean // true = require JWT, false = bypass JWT
|
|
520
522
|
| {
|
|
521
523
|
required?: boolean // Default: true if global enabled
|
|
524
|
+
requireService?: boolean // Require service JWT token in X-Service-Authorization header
|
|
522
525
|
authorize?: JWTAuthorizationHandler // Custom authorization logic
|
|
523
526
|
}
|
|
524
527
|
}
|
|
@@ -754,103 +757,123 @@ const jwtService = new FastifyJwtService<
|
|
|
754
757
|
})
|
|
755
758
|
```
|
|
756
759
|
|
|
757
|
-
##
|
|
760
|
+
## Service-to-Service Authentication
|
|
758
761
|
|
|
759
|
-
|
|
762
|
+
For microservices architecture, the package now supports service JWT tokens for secure service-to-service communication. This allows the gateway to authenticate requests to backend services.
|
|
763
|
+
|
|
764
|
+
### Service JWT Token Options
|
|
765
|
+
|
|
766
|
+
When initializing `FastifyJwtService`, add `serviceTokenExpiry`:
|
|
760
767
|
|
|
761
768
|
```typescript
|
|
762
|
-
const
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
769
|
+
const jwtService = new FastifyJwtService(app, {
|
|
770
|
+
accessTokenExpiry: '15m',
|
|
771
|
+
refreshTokenExpiry: '7d',
|
|
772
|
+
renewalThreshold: '1d',
|
|
773
|
+
serviceTokenExpiry: '1h', // NEW: Service token lifetime
|
|
767
774
|
})
|
|
775
|
+
```
|
|
768
776
|
|
|
769
|
-
|
|
770
|
-
app.route({
|
|
771
|
-
method: 'POST',
|
|
772
|
-
url: '/auth/login',
|
|
773
|
-
config: { jwt: false },
|
|
774
|
-
handler: async () => { /* ... */ },
|
|
775
|
-
})
|
|
777
|
+
### Generate Service Token
|
|
776
778
|
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
handler: async () => { /* ... */ },
|
|
782
|
-
})
|
|
779
|
+
```typescript
|
|
780
|
+
// Generate a token for service-to-service communication
|
|
781
|
+
const serviceToken = jwtService.generateServiceToken('gateway')
|
|
782
|
+
// Returns a JWT token with payload: { serviceId: 'gateway', type: 'service', exp: <timestamp> }
|
|
783
783
|
```
|
|
784
784
|
|
|
785
|
-
###
|
|
785
|
+
### Require Service Token on Route
|
|
786
|
+
|
|
787
|
+
Protect a route to require service JWT authentication in the `X-Service-Authorization` header:
|
|
786
788
|
|
|
787
789
|
```typescript
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
790
|
+
app.route({
|
|
791
|
+
method: 'PATCH',
|
|
792
|
+
url: '/v1/credentials/change-password',
|
|
793
|
+
config: {
|
|
794
|
+
jwt: {
|
|
795
|
+
requireService: true, // NEW: Require service JWT token
|
|
796
|
+
},
|
|
797
|
+
},
|
|
798
|
+
handler: async (request, reply) => {
|
|
799
|
+
// Both user JWT and service JWT are validated
|
|
800
|
+
const user = request.user // User info from client JWT
|
|
801
|
+
return reply.send(formatSuccess(200, { success: true }))
|
|
792
802
|
},
|
|
793
803
|
})
|
|
804
|
+
```
|
|
794
805
|
|
|
795
|
-
|
|
796
|
-
app.route({
|
|
797
|
-
method: 'GET',
|
|
798
|
-
url: '/public',
|
|
799
|
-
handler: async () => { /* ... */ },
|
|
800
|
-
})
|
|
806
|
+
### Service Token Flow
|
|
801
807
|
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
+
In a gateway-to-service architecture:
|
|
809
|
+
|
|
810
|
+
```typescript
|
|
811
|
+
// Gateway generates service token
|
|
812
|
+
const serviceToken = jwtService.generateServiceToken('gateway')
|
|
813
|
+
|
|
814
|
+
// Gateway forwards request with both tokens:
|
|
815
|
+
fetch('http://auth-service.local/v1/credentials/change-password', {
|
|
816
|
+
method: 'PATCH',
|
|
817
|
+
headers: {
|
|
818
|
+
'Authorization': `Bearer ${userJwtToken}`, // User identity
|
|
819
|
+
'X-Service-Authorization': `Bearer ${serviceToken}`, // Gateway authentication
|
|
820
|
+
},
|
|
821
|
+
body: JSON.stringify(data),
|
|
808
822
|
})
|
|
823
|
+
|
|
824
|
+
// Backend service validates both:
|
|
825
|
+
// 1. X-Service-Authorization header - confirms request from authorized gateway
|
|
826
|
+
// 2. Authorization header - identifies which user is making the request
|
|
809
827
|
```
|
|
810
828
|
|
|
811
|
-
###
|
|
829
|
+
### Complete Example: Gateway with Service Authentication
|
|
812
830
|
|
|
813
831
|
```typescript
|
|
832
|
+
import { createFastify, FastifyJwtService } from '@a_jackie_z/fastify'
|
|
833
|
+
|
|
814
834
|
const app = await createFastify({
|
|
815
835
|
jwt: {
|
|
816
|
-
secret: '
|
|
836
|
+
secret: 'shared-secret-key', // Shared with backend services
|
|
817
837
|
global: true,
|
|
818
838
|
},
|
|
819
839
|
})
|
|
820
840
|
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
return request.user
|
|
827
|
-
},
|
|
841
|
+
const jwtService = new FastifyJwtService(app, {
|
|
842
|
+
accessTokenExpiry: '15m',
|
|
843
|
+
refreshTokenExpiry: '7d',
|
|
844
|
+
renewalThreshold: '1d',
|
|
845
|
+
serviceTokenExpiry: '1h',
|
|
828
846
|
})
|
|
829
847
|
|
|
830
|
-
//
|
|
848
|
+
// Forward request to backend service
|
|
831
849
|
app.route({
|
|
832
|
-
method: '
|
|
833
|
-
url: '/
|
|
834
|
-
config: {
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
850
|
+
method: 'PATCH',
|
|
851
|
+
url: '/auth/v1/credentials/change-password',
|
|
852
|
+
config: { jwt: true }, // Require user JWT
|
|
853
|
+
handler: async (request, reply) => {
|
|
854
|
+
// Extract user token from request
|
|
855
|
+
const userToken = request.headers.authorization?.replace('Bearer ', '')
|
|
856
|
+
|
|
857
|
+
// Generate service token for gateway
|
|
858
|
+
const serviceToken = jwtService.generateServiceToken('gateway')
|
|
859
|
+
|
|
860
|
+
// Forward to backend service
|
|
861
|
+
const response = await fetch('http://auth-service:8200/v1/credentials/change-password', {
|
|
862
|
+
method: 'PATCH',
|
|
863
|
+
headers: {
|
|
864
|
+
'Authorization': `Bearer ${userToken}`, // Forward user token
|
|
865
|
+
'X-Service-Authorization': `Bearer ${serviceToken}`, // Add service token
|
|
866
|
+
'Content-Type': 'application/json',
|
|
867
|
+
},
|
|
868
|
+
body: JSON.stringify(request.body),
|
|
869
|
+
})
|
|
870
|
+
|
|
871
|
+
const data = await response.json()
|
|
872
|
+
return reply.status(response.status).send(data)
|
|
838
873
|
},
|
|
839
|
-
handler: async () => { /* ... */ },
|
|
840
874
|
})
|
|
841
875
|
|
|
842
|
-
|
|
843
|
-
app.route({
|
|
844
|
-
method: 'DELETE',
|
|
845
|
-
url: '/admin/system',
|
|
846
|
-
config: {
|
|
847
|
-
jwt: {
|
|
848
|
-
authorize: async (_req, _reply, user) =>
|
|
849
|
-
user?.role === 'admin' && user?.permissions?.includes('system:delete'),
|
|
850
|
-
},
|
|
851
|
-
},
|
|
852
|
-
handler: async () => { /* ... */ },
|
|
853
|
-
})
|
|
876
|
+
await runFastify(app, '0.0.0.0', 3000)
|
|
854
877
|
```
|
|
855
878
|
|
|
856
879
|
## Testing Your API
|
package/dist/index.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ type JWTAuthorizationHandler = <TUser = unknown>(request: FastifyRequest, reply:
|
|
|
12
12
|
interface JWTRouteConfig {
|
|
13
13
|
jwt?: boolean | {
|
|
14
14
|
required?: boolean;
|
|
15
|
+
requireService?: boolean;
|
|
15
16
|
authorize?: JWTAuthorizationHandler;
|
|
16
17
|
};
|
|
17
18
|
}
|
|
@@ -19,6 +20,7 @@ declare module 'fastify' {
|
|
|
19
20
|
interface FastifyContextConfig {
|
|
20
21
|
jwt?: boolean | {
|
|
21
22
|
required?: boolean;
|
|
23
|
+
requireService?: boolean;
|
|
22
24
|
authorize?: JWTAuthorizationHandler;
|
|
23
25
|
};
|
|
24
26
|
}
|
|
@@ -96,11 +98,20 @@ declare const baseRefreshTokenPayloadSchema: z.ZodObject<{
|
|
|
96
98
|
type: z.ZodLiteral<"refresh">;
|
|
97
99
|
exp: z.ZodNumber;
|
|
98
100
|
}, z.core.$strip>;
|
|
101
|
+
/**
|
|
102
|
+
* Service-to-service authentication token payload schema
|
|
103
|
+
*/
|
|
104
|
+
declare const serviceAccessTokenPayloadSchema: z.ZodObject<{
|
|
105
|
+
serviceId: z.ZodString;
|
|
106
|
+
type: z.ZodLiteral<"service">;
|
|
107
|
+
exp: z.ZodNumber;
|
|
108
|
+
}, z.core.$strip>;
|
|
99
109
|
/**
|
|
100
110
|
* Inferred types from base schemas
|
|
101
111
|
*/
|
|
102
112
|
type BaseAccessTokenPayload = z.infer<typeof baseAccessTokenPayloadSchema>;
|
|
103
113
|
type BaseRefreshTokenPayload = z.infer<typeof baseRefreshTokenPayloadSchema>;
|
|
114
|
+
type ServiceAccessTokenPayload = z.infer<typeof serviceAccessTokenPayloadSchema>;
|
|
104
115
|
/**
|
|
105
116
|
* Token pair interface
|
|
106
117
|
*/
|
|
@@ -115,6 +126,7 @@ interface FastifyJwtServiceOptions {
|
|
|
115
126
|
accessTokenExpiry: string;
|
|
116
127
|
refreshTokenExpiry: string;
|
|
117
128
|
renewalThreshold: string;
|
|
129
|
+
serviceTokenExpiry: string;
|
|
118
130
|
accessTokenSchema?: z.ZodSchema;
|
|
119
131
|
refreshTokenSchema?: z.ZodSchema;
|
|
120
132
|
}
|
|
@@ -149,6 +161,7 @@ declare class FastifyJwtService<TAccess extends BaseAccessTokenPayload = BaseAcc
|
|
|
149
161
|
private readonly accessTokenExpiry;
|
|
150
162
|
private readonly refreshTokenExpiry;
|
|
151
163
|
private readonly renewalThreshold;
|
|
164
|
+
private readonly serviceTokenExpiry;
|
|
152
165
|
private readonly accessTokenSchema;
|
|
153
166
|
private readonly refreshTokenSchema;
|
|
154
167
|
constructor(app: FastifyServer, options: FastifyJwtServiceOptions);
|
|
@@ -164,6 +177,12 @@ declare class FastifyJwtService<TAccess extends BaseAccessTokenPayload = BaseAcc
|
|
|
164
177
|
* Generate both access and refresh tokens
|
|
165
178
|
*/
|
|
166
179
|
generateTokenPair(identityId: string): TokenPair;
|
|
180
|
+
/**
|
|
181
|
+
* Generate a service-to-service authentication token
|
|
182
|
+
* @param serviceId - The identifier of the service (e.g., 'gateway', 'auth-service')
|
|
183
|
+
* @returns JWT token valid for service-to-service communication
|
|
184
|
+
*/
|
|
185
|
+
generateServiceToken(serviceId: string): string;
|
|
167
186
|
/**
|
|
168
187
|
* Verify and decode an access token
|
|
169
188
|
*/
|
|
@@ -198,4 +217,4 @@ interface CreateErrorOptions {
|
|
|
198
217
|
declare function createError(options: CreateErrorOptions): FastifyError;
|
|
199
218
|
declare function setupErrorHandler(fastify: FastifyInstance): void;
|
|
200
219
|
|
|
201
|
-
export { type BaseAccessTokenPayload, type BaseRefreshTokenPayload, type CreateErrorOptions, type CreateFastifyOptions, type ErrorResponse, FastifyJwtService, type FastifyJwtServiceOptions, type FastifyServer, type JWTAuthorizationHandler, type JWTRouteConfig, type SuccessResponse, type TokenPair, type ValidationDetail, baseAccessTokenPayloadSchema, baseRefreshTokenPayloadSchema, createError, createFastify, createFastifyPlugin, errorResponseSchema, formatError, formatSuccess, healthPlugin, runFastify, setupErrorHandler, successResponseSchema };
|
|
220
|
+
export { type BaseAccessTokenPayload, type BaseRefreshTokenPayload, type CreateErrorOptions, type CreateFastifyOptions, type ErrorResponse, FastifyJwtService, type FastifyJwtServiceOptions, type FastifyServer, type JWTAuthorizationHandler, type JWTRouteConfig, type ServiceAccessTokenPayload, type SuccessResponse, type TokenPair, type ValidationDetail, baseAccessTokenPayloadSchema, baseRefreshTokenPayloadSchema, createError, createFastify, createFastifyPlugin, errorResponseSchema, formatError, formatSuccess, healthPlugin, runFastify, serviceAccessTokenPayloadSchema, setupErrorHandler, successResponseSchema };
|
package/dist/index.js
CHANGED
|
@@ -198,6 +198,33 @@ async function setupJWT(fastify, options, swaggerRoutePrefix) {
|
|
|
198
198
|
if (shouldVerify) {
|
|
199
199
|
try {
|
|
200
200
|
await request.jwtVerify();
|
|
201
|
+
if (typeof jwtConfig === "object" && jwtConfig.requireService) {
|
|
202
|
+
const serviceAuthHeader = request.headers["x-service-authorization"];
|
|
203
|
+
if (!serviceAuthHeader) {
|
|
204
|
+
return reply.status(401).send(
|
|
205
|
+
formatError(401, "Unauthorized", "Missing service authorization token")
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
const serviceToken = typeof serviceAuthHeader === "string" ? serviceAuthHeader.replace("Bearer ", "") : serviceAuthHeader[0]?.replace("Bearer ", "");
|
|
209
|
+
if (!serviceToken) {
|
|
210
|
+
return reply.status(401).send(
|
|
211
|
+
formatError(401, "Unauthorized", "Invalid service authorization header format")
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
try {
|
|
215
|
+
const servicePayload = fastify.jwt.verify(serviceToken);
|
|
216
|
+
if (servicePayload.type !== "service") {
|
|
217
|
+
return reply.status(401).send(
|
|
218
|
+
formatError(401, "Unauthorized", "Invalid service token type")
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
request.serviceUser = servicePayload;
|
|
222
|
+
} catch (err) {
|
|
223
|
+
return reply.status(401).send(
|
|
224
|
+
formatError(401, "Unauthorized", "Invalid service authorization token")
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
201
228
|
if (typeof jwtConfig === "object" && jwtConfig.authorize) {
|
|
202
229
|
const authorized = await jwtConfig.authorize(request, reply, request.user);
|
|
203
230
|
if (!authorized) {
|
|
@@ -258,18 +285,25 @@ var baseRefreshTokenPayloadSchema = z2.object({
|
|
|
258
285
|
type: z2.literal("refresh"),
|
|
259
286
|
exp: z2.number()
|
|
260
287
|
});
|
|
288
|
+
var serviceAccessTokenPayloadSchema = z2.object({
|
|
289
|
+
serviceId: z2.string(),
|
|
290
|
+
type: z2.literal("service"),
|
|
291
|
+
exp: z2.number()
|
|
292
|
+
});
|
|
261
293
|
var FastifyJwtService = class {
|
|
262
294
|
constructor(app, options) {
|
|
263
295
|
this.app = app;
|
|
264
296
|
this.accessTokenExpiry = options.accessTokenExpiry;
|
|
265
297
|
this.refreshTokenExpiry = options.refreshTokenExpiry;
|
|
266
298
|
this.renewalThreshold = options.renewalThreshold;
|
|
299
|
+
this.serviceTokenExpiry = options.serviceTokenExpiry;
|
|
267
300
|
this.accessTokenSchema = options.accessTokenSchema ?? baseAccessTokenPayloadSchema;
|
|
268
301
|
this.refreshTokenSchema = options.refreshTokenSchema ?? baseRefreshTokenPayloadSchema;
|
|
269
302
|
}
|
|
270
303
|
accessTokenExpiry;
|
|
271
304
|
refreshTokenExpiry;
|
|
272
305
|
renewalThreshold;
|
|
306
|
+
serviceTokenExpiry;
|
|
273
307
|
accessTokenSchema;
|
|
274
308
|
refreshTokenSchema;
|
|
275
309
|
/**
|
|
@@ -307,6 +341,20 @@ var FastifyJwtService = class {
|
|
|
307
341
|
refreshToken: this.generateRefreshToken(identityId)
|
|
308
342
|
};
|
|
309
343
|
}
|
|
344
|
+
/**
|
|
345
|
+
* Generate a service-to-service authentication token
|
|
346
|
+
* @param serviceId - The identifier of the service (e.g., 'gateway', 'auth-service')
|
|
347
|
+
* @returns JWT token valid for service-to-service communication
|
|
348
|
+
*/
|
|
349
|
+
generateServiceToken(serviceId) {
|
|
350
|
+
const payload = {
|
|
351
|
+
serviceId,
|
|
352
|
+
type: "service"
|
|
353
|
+
};
|
|
354
|
+
return this.app.jwt.sign(payload, {
|
|
355
|
+
expiresIn: this.serviceTokenExpiry
|
|
356
|
+
});
|
|
357
|
+
}
|
|
310
358
|
/**
|
|
311
359
|
* Verify and decode an access token
|
|
312
360
|
*/
|
|
@@ -430,6 +478,7 @@ export {
|
|
|
430
478
|
formatSuccess,
|
|
431
479
|
healthPlugin,
|
|
432
480
|
runFastify,
|
|
481
|
+
serviceAccessTokenPayloadSchema,
|
|
433
482
|
setupErrorHandler,
|
|
434
483
|
successResponseSchema
|
|
435
484
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../lib/fastify.ts","../lib/response.ts","../lib/error-handler.ts","../lib/swagger-setup.ts","../lib/jwt-setup.ts","../lib/jwt.service.ts","../lib/plugin.ts","../lib/plugins/healthPlugin.ts","../index.ts"],"sourcesContent":["import Fastify, { FastifyServerOptions } from 'fastify'\nimport { serializerCompiler, validatorCompiler, ZodTypeProvider } from 'fastify-type-provider-zod'\nimport fastifyRateLimit from '@fastify/rate-limit'\nimport { setupErrorHandler } from './error-handler.ts'\nimport { setupSwagger, setupSwaggerSecurityHook } from './swagger-setup.ts'\nimport { setupJWT } from './jwt-setup.ts'\nimport type { CreateFastifyOptions, FastifyServer } from './types.ts'\n\n// Re-export types and utilities from other modules\nexport * from './response.ts'\nexport * from './types.ts'\n\nexport async function createFastify(options?: CreateFastifyOptions): Promise<FastifyServer> {\n const fastifyOptions: FastifyServerOptions = {}\n\n if (options?.logger) {\n fastifyOptions.loggerInstance = options.logger\n }\n\n const fastify = Fastify(fastifyOptions).withTypeProvider<ZodTypeProvider>()\n\n // Set up Zod validation and serialization\n fastify.setValidatorCompiler(validatorCompiler)\n fastify.setSerializerCompiler(serializerCompiler)\n\n // Set up error handler for standardized error responses\n setupErrorHandler(fastify)\n\n // Register Swagger first to capture all routes with Zod schemas\n const swaggerRoutePrefix = await setupSwagger(fastify, options || {})\n\n // Auto-inject security requirements for JWT-protected routes\n setupSwaggerSecurityHook(fastify, options || {}, swaggerRoutePrefix)\n\n // Register JWT authentication\n await setupJWT(fastify, options || {}, swaggerRoutePrefix)\n\n // Register Rate Limiting\n if (options?.rateLimit?.global) {\n await fastify.register(fastifyRateLimit, {\n global: true,\n ...options.rateLimit.global,\n })\n }\n\n return fastify\n}\n\nexport async function runFastify(fastify: FastifyServer, host: string, port: number) {\n try {\n await fastify.listen({ host, port })\n } catch (err) {\n fastify.log.error(err)\n process.exit(1)\n }\n}\n","import { z } from 'zod'\n\n// Response Types\nexport interface ValidationDetail {\n field: string\n message: string\n}\n\nexport interface SuccessResponse<T> {\n status: number\n success: true\n data: T\n}\n\nexport interface ErrorResponse {\n status: number\n success: false\n error: string\n message: string\n details?: ValidationDetail[]\n}\n\n// Response Formatters\nexport function formatSuccess<T>(status: number, data: T): SuccessResponse<T> {\n return {\n status,\n success: true,\n data,\n }\n}\n\nexport function formatError(\n status: number,\n error: string,\n message: string,\n details?: ValidationDetail[]\n): ErrorResponse {\n const response: ErrorResponse = {\n status,\n success: false,\n error,\n message,\n }\n if (details && details.length > 0) {\n response.details = details\n }\n return response\n}\n\n// Zod Schema Helpers for Standardized Responses\nexport const successResponseSchema = <T extends z.ZodTypeAny>(dataSchema: T) => z.object({\n status: z.number(),\n success: z.literal(true),\n data: dataSchema,\n})\n\nexport const errorResponseSchema = z.object({\n status: z.number(),\n success: z.literal(false),\n error: z.string(),\n message: z.string(),\n details: z.array(z.object({\n field: z.string(),\n message: z.string(),\n })).optional(),\n})\n","import type { FastifyError, FastifyInstance } from 'fastify'\nimport { formatError, type ValidationDetail } from './response.ts'\n\nexport interface CreateErrorOptions {\n statusCode?: number\n message: string\n name?: string\n}\n\nexport function createError(options: CreateErrorOptions): FastifyError {\n const {\n statusCode = 500,\n message,\n name = 'Error',\n } = options\n\n const error = new Error(message) as FastifyError\n error.statusCode = statusCode\n error.name = name\n return error\n}\n\nexport function setupErrorHandler(fastify: FastifyInstance): void {\n fastify.setErrorHandler((error: FastifyError, request, reply) => {\n // Log all errors\n fastify.log.error({\n err: error,\n url: request.url,\n method: request.method,\n }, 'Request error')\n\n // Handle Zod validation errors\n if (error.validation) {\n const details: ValidationDetail[] = error.validation.map((issue: any) => {\n // Build field path from dataPath or instancePath\n const field = issue.instancePath || issue.dataPath || issue.params?.missingProperty || 'unknown'\n const cleanField = field.startsWith('/') ? field.slice(1).replace(/\\//g, '.') : field\n\n return {\n field: cleanField || 'unknown',\n message: issue.message || 'Validation failed',\n }\n })\n\n return reply.status(400).send(\n formatError(400, 'Validation Error', 'Request validation failed', details)\n )\n }\n\n // Handle rate limit errors\n if (error.statusCode === 429) {\n return reply.status(429).send(\n formatError(429, 'Too Many Requests', 'Rate limit exceeded')\n )\n }\n\n // Handle authentication errors\n if (error.statusCode === 401) {\n return reply.status(401).send(\n formatError(401, 'Unauthorized', error.message || 'Authentication required')\n )\n }\n\n // Handle authorization errors\n if (error.statusCode === 403) {\n return reply.status(403).send(\n formatError(403, 'Forbidden', error.message || 'Access denied')\n )\n }\n\n // Handle not found errors\n if (error.statusCode === 404) {\n return reply.status(404).send(\n formatError(404, 'Not Found', error.message || 'Resource not found')\n )\n }\n\n // Handle all other errors as internal server errors\n const statusCode = error.statusCode || 500\n return reply.status(statusCode).send(\n formatError(\n statusCode,\n statusCode === 500 ? 'Internal Server Error' : error.name || 'Error',\n error.message || 'An unexpected error occurred'\n )\n )\n })\n}\n","import type { FastifyInstance } from 'fastify'\nimport fastifySwagger, { SwaggerOptions } from '@fastify/swagger'\nimport fastifySwaggerUI, { FastifySwaggerUiOptions } from '@fastify/swagger-ui'\nimport { jsonSchemaTransform } from 'fastify-type-provider-zod'\nimport type { CreateFastifyOptions, JWTRouteConfig } from './types.ts'\n\nexport async function setupSwagger(\n fastify: FastifyInstance,\n options: CreateFastifyOptions\n): Promise<string | undefined> {\n if (!options.swagger) {\n return undefined\n }\n\n const openApiConfig: any = {\n openapi: {\n info: {\n title: options.swagger.title,\n version: options.swagger.version,\n description: options.swagger.description,\n },\n },\n transform: jsonSchemaTransform,\n }\n\n // Add bearer auth security scheme if JWT is enabled\n if (options.jwt) {\n openApiConfig.openapi.components = {\n securitySchemes: {\n bearerAuth: {\n type: 'http',\n scheme: 'bearer',\n bearerFormat: 'JWT',\n },\n },\n }\n }\n\n await fastify.register(fastifySwagger, openApiConfig as SwaggerOptions)\n\n let routePrefix = options.swagger.routePrefix || '/docs/'\n\n if (!routePrefix.startsWith('/')) {\n routePrefix = '/' + routePrefix\n }\n\n if (!routePrefix.endsWith('/')) {\n routePrefix = routePrefix + '/'\n }\n\n await fastify.register(fastifySwaggerUI, { routePrefix } as FastifySwaggerUiOptions)\n\n return routePrefix\n}\n\nexport function setupSwaggerSecurityHook(\n fastify: FastifyInstance,\n options: CreateFastifyOptions,\n swaggerRoutePrefix: string | undefined\n): void {\n if (!options.jwt || !options.swagger) {\n return\n }\n\n // Auto-inject security requirements for JWT-protected routes\n fastify.addHook('onRoute', (routeOptions) => {\n // Skip Swagger routes\n if (swaggerRoutePrefix && routeOptions.url.startsWith(swaggerRoutePrefix)) {\n return\n }\n\n const routeConfig = (routeOptions.config as JWTRouteConfig) || {}\n const jwtConfig = routeConfig.jwt\n\n // Skip if JWT is explicitly bypassed\n if (jwtConfig === false) {\n return\n }\n\n // Determine if route requires JWT\n const requiresJWT =\n jwtConfig === true ||\n (typeof jwtConfig === 'object' && jwtConfig.required !== false) ||\n (jwtConfig === undefined && options.jwt?.global === true)\n\n // Inject security requirement if JWT is required\n if (requiresJWT) {\n if (!routeOptions.schema) {\n routeOptions.schema = {}\n }\n if (!routeOptions.schema.security) {\n routeOptions.schema.security = [{ bearerAuth: [] }]\n }\n }\n })\n}\n","import type { FastifyInstance } from 'fastify'\nimport fastifyJwt, { FastifyJWTOptions } from '@fastify/jwt'\nimport { formatError } from './response.ts'\nimport type { CreateFastifyOptions, JWTRouteConfig } from './types.ts'\n\nexport async function setupJWT(\n fastify: FastifyInstance,\n options: CreateFastifyOptions,\n swaggerRoutePrefix: string | undefined\n): Promise<void> {\n if (!options.jwt) {\n return\n }\n\n const jwtOptions: FastifyJWTOptions = {\n secret: options.jwt.secret,\n }\n if (options.jwt.sign !== undefined) {\n jwtOptions.sign = options.jwt.sign\n }\n if (options.jwt.verify !== undefined) {\n jwtOptions.verify = options.jwt.verify\n }\n await fastify.register(fastifyJwt, jwtOptions)\n\n // Global JWT checking hook\n if (options.jwt.global) {\n fastify.addHook('onRequest', async (request, reply) => {\n // Skip JWT verification for Swagger documentation routes\n if (swaggerRoutePrefix && request.url.startsWith(swaggerRoutePrefix)) {\n return\n }\n\n const routeConfig = (request.routeOptions.config as JWTRouteConfig) || {}\n const jwtConfig = routeConfig.jwt\n\n // Check if route explicitly bypasses JWT\n if (jwtConfig === false) {\n return\n }\n\n // Check if route explicitly requires JWT or uses global setting\n const shouldVerify =\n jwtConfig === true ||\n (typeof jwtConfig === 'object' && jwtConfig.required !== false) ||\n (jwtConfig === undefined && options.jwt?.global === true)\n\n if (shouldVerify) {\n try {\n await request.jwtVerify()\n\n // Custom authorization - check JWT info before route processing\n if (typeof jwtConfig === 'object' && jwtConfig.authorize) {\n const authorized = await jwtConfig.authorize(request, reply, request.user)\n if (!authorized) {\n return reply.status(403).send(\n formatError(403, 'Forbidden', 'Authorization failed')\n )\n }\n }\n } catch (err) {\n return reply.status(401).send(\n formatError(401, 'Unauthorized', 'Invalid or missing JWT token')\n )\n }\n }\n })\n }\n}\n","import { z } from 'zod'\nimport { FastifyServer } from './fastify.ts'\n\n/**\n * Base access token payload schema\n */\nexport const baseAccessTokenPayloadSchema = z.object({\n identityId: z.string(),\n type: z.literal('access'),\n exp: z.number(),\n})\n\n/**\n * Base refresh token payload schema\n */\nexport const baseRefreshTokenPayloadSchema = z.object({\n identityId: z.string(),\n type: z.literal('refresh'),\n exp: z.number(),\n})\n\n/**\n * Inferred types from base schemas\n */\nexport type BaseAccessTokenPayload = z.infer<typeof baseAccessTokenPayloadSchema>\nexport type BaseRefreshTokenPayload = z.infer<typeof baseRefreshTokenPayloadSchema>\n\n/**\n * Token pair interface\n */\nexport interface TokenPair {\n accessToken: string\n refreshToken: string\n}\n\n/**\n * FastifyJwtService configuration options\n */\nexport interface FastifyJwtServiceOptions {\n accessTokenExpiry: string\n refreshTokenExpiry: string\n renewalThreshold: string\n accessTokenSchema?: z.ZodSchema\n refreshTokenSchema?: z.ZodSchema\n}\n\n/**\n * Generic JWT service for Fastify applications\n *\n * @template TAccess - Access token payload type extending BaseAccessTokenPayload\n * @template TRefresh - Refresh token payload type extending BaseRefreshTokenPayload\n *\n * @example\n * // Using base schemas\n * const jwtService = new FastifyJwtService(app, {\n * accessTokenExpiry: '15m',\n * refreshTokenExpiry: '7d',\n * renewalThreshold: '1d',\n * })\n *\n * @example\n * // Using extended schemas\n * const customAccessSchema = baseAccessTokenPayloadSchema.extend({\n * roles: z.array(z.string())\n * })\n * const jwtService = new FastifyJwtService(app, {\n * accessTokenExpiry: '15m',\n * refreshTokenExpiry: '7d',\n * renewalThreshold: '1d',\n * accessTokenSchema: customAccessSchema,\n * })\n */\nexport class FastifyJwtService<\n TAccess extends BaseAccessTokenPayload = BaseAccessTokenPayload,\n TRefresh extends BaseRefreshTokenPayload = BaseRefreshTokenPayload\n> {\n private readonly accessTokenExpiry: string\n private readonly refreshTokenExpiry: string\n private readonly renewalThreshold: string\n private readonly accessTokenSchema: z.ZodSchema<TAccess>\n private readonly refreshTokenSchema: z.ZodSchema<TRefresh>\n\n constructor(\n private readonly app: FastifyServer,\n options: FastifyJwtServiceOptions\n ) {\n this.accessTokenExpiry = options.accessTokenExpiry\n this.refreshTokenExpiry = options.refreshTokenExpiry\n this.renewalThreshold = options.renewalThreshold\n this.accessTokenSchema = (options.accessTokenSchema ?? baseAccessTokenPayloadSchema) as z.ZodSchema<TAccess>\n this.refreshTokenSchema = (options.refreshTokenSchema ?? baseRefreshTokenPayloadSchema) as z.ZodSchema<TRefresh>\n }\n\n /**\n * Generate an access token for the given identity\n */\n generateAccessToken(identityId: string, extraPayload?: Partial<Omit<TAccess, 'identityId' | 'type' | 'exp'>>): string {\n const payload = {\n identityId,\n type: 'access' as const,\n ...extraPayload,\n }\n\n return this.app.jwt.sign(payload, {\n expiresIn: this.accessTokenExpiry,\n })\n }\n\n /**\n * Generate a refresh token for the given identity\n */\n generateRefreshToken(identityId: string, extraPayload?: Partial<Omit<TRefresh, 'identityId' | 'type' | 'exp'>>): string {\n const payload = {\n identityId,\n type: 'refresh' as const,\n ...extraPayload,\n }\n\n return this.app.jwt.sign(payload, {\n expiresIn: this.refreshTokenExpiry,\n })\n }\n\n /**\n * Generate both access and refresh tokens\n */\n generateTokenPair(identityId: string): TokenPair {\n return {\n accessToken: this.generateAccessToken(identityId),\n refreshToken: this.generateRefreshToken(identityId),\n }\n }\n\n /**\n * Verify and decode an access token\n */\n verifyAccessToken(token: string): TAccess {\n try {\n const payload = this.app.jwt.verify(token) as unknown\n const validated = this.accessTokenSchema.parse(payload)\n\n if (validated.type !== 'access') {\n throw new Error('Invalid token type')\n }\n\n return validated\n } catch (error) {\n if (error instanceof z.ZodError) {\n throw new Error('Invalid token payload')\n }\n throw error\n }\n }\n\n /**\n * Verify and decode a refresh token\n */\n verifyRefreshToken(token: string): TRefresh {\n try {\n const payload = this.app.jwt.verify(token) as unknown\n const validated = this.refreshTokenSchema.parse(payload)\n\n if (validated.type !== 'refresh') {\n throw new Error('Invalid token type')\n }\n\n return validated\n } catch (error) {\n if (error instanceof z.ZodError) {\n throw new Error('Invalid token payload')\n }\n throw error\n }\n }\n\n /**\n * Renew a refresh token for the given identity\n */\n renewRefreshToken(identityId: string): string {\n return this.generateRefreshToken(identityId)\n }\n\n /**\n * Check if a refresh token should be renewed based on its expiry\n */\n shouldRenewRefreshToken(exp: number): boolean {\n const now = Math.floor(Date.now() / 1000)\n const threshold = this.parseTimespan(this.renewalThreshold)\n return (exp - now) < threshold\n }\n\n /**\n * Parse timespan string to seconds\n */\n private parseTimespan(timespan: string): number {\n const match = timespan.match(/^(\\d+)([smhd])$/)\n if (!match || !match[1] || !match[2]) {\n throw new Error(`Invalid timespan format: ${timespan}`)\n }\n\n const value = parseInt(match[1]!, 10)\n const unit = match[2]!\n\n switch (unit) {\n case 's':\n return value\n case 'm':\n return value * 60\n case 'h':\n return value * 3600\n case 'd':\n return value * 86400\n default:\n throw new Error(`Unknown timespan unit: ${unit}`)\n }\n }\n}\n","import { FastifyPluginCallback } from 'fastify'\nimport { FastifyServer } from './fastify.ts'\nimport { ZodTypeProvider } from 'fastify-type-provider-zod'\n\nexport function createFastifyPlugin(cb: FastifyPluginCallback) {\n return function createFastifyPluginWrapper(\n fastify: FastifyServer,\n options: Parameters<FastifyPluginCallback>[1],\n done: Parameters<FastifyPluginCallback>[2],\n ) {\n const server = fastify.withTypeProvider<ZodTypeProvider>()\n let doneCalled = false\n\n const doneWrapper = (err?: Error) => {\n done(err)\n doneCalled = true\n }\n\n cb(server, options, doneWrapper)\n\n if (!doneCalled) {\n done()\n }\n }\n}\n","import { createFastifyPlugin } from '../plugin.ts'\nimport { FastifyServer } from '../fastify.ts'\n\nexport const healthPlugin = createFastifyPlugin((app: FastifyServer) => {\n app.get('/v1/health', {\n schema: {\n tags: ['health'],\n },\n config: {\n jwt: false,\n },\n handler: async () => {\n return {\n status: 200,\n message: 'ok',\n }\n },\n })\n})\n","export * from './lib/fastify.ts'\nexport * from './lib/response.ts'\nexport * from './lib/types.ts'\nexport * from './lib/jwt.service.ts'\nexport * from './lib/plugin.ts'\nexport * from './lib/plugins/healthPlugin.ts'\nexport * from './lib/error-handler.ts'\nexport * from 'fastify-type-provider-zod'\nexport type { FastifyErrorCodes, FastifyRequest, FastifyReply } from 'fastify'\n"],"mappings":"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","names":["z"]}
|
|
1
|
+
{"version":3,"sources":["../lib/fastify.ts","../lib/response.ts","../lib/error-handler.ts","../lib/swagger-setup.ts","../lib/jwt-setup.ts","../lib/jwt.service.ts","../lib/plugin.ts","../lib/plugins/healthPlugin.ts","../index.ts"],"sourcesContent":["import Fastify, { FastifyServerOptions } from 'fastify'\nimport { serializerCompiler, validatorCompiler, ZodTypeProvider } from 'fastify-type-provider-zod'\nimport fastifyRateLimit from '@fastify/rate-limit'\nimport { setupErrorHandler } from './error-handler.ts'\nimport { setupSwagger, setupSwaggerSecurityHook } from './swagger-setup.ts'\nimport { setupJWT } from './jwt-setup.ts'\nimport type { CreateFastifyOptions, FastifyServer } from './types.ts'\n\n// Re-export types and utilities from other modules\nexport * from './response.ts'\nexport * from './types.ts'\n\nexport async function createFastify(options?: CreateFastifyOptions): Promise<FastifyServer> {\n const fastifyOptions: FastifyServerOptions = {}\n\n if (options?.logger) {\n fastifyOptions.loggerInstance = options.logger\n }\n\n const fastify = Fastify(fastifyOptions).withTypeProvider<ZodTypeProvider>()\n\n // Set up Zod validation and serialization\n fastify.setValidatorCompiler(validatorCompiler)\n fastify.setSerializerCompiler(serializerCompiler)\n\n // Set up error handler for standardized error responses\n setupErrorHandler(fastify)\n\n // Register Swagger first to capture all routes with Zod schemas\n const swaggerRoutePrefix = await setupSwagger(fastify, options || {})\n\n // Auto-inject security requirements for JWT-protected routes\n setupSwaggerSecurityHook(fastify, options || {}, swaggerRoutePrefix)\n\n // Register JWT authentication\n await setupJWT(fastify, options || {}, swaggerRoutePrefix)\n\n // Register Rate Limiting\n if (options?.rateLimit?.global) {\n await fastify.register(fastifyRateLimit, {\n global: true,\n ...options.rateLimit.global,\n })\n }\n\n return fastify\n}\n\nexport async function runFastify(fastify: FastifyServer, host: string, port: number) {\n try {\n await fastify.listen({ host, port })\n } catch (err) {\n fastify.log.error(err)\n process.exit(1)\n }\n}\n","import { z } from 'zod'\n\n// Response Types\nexport interface ValidationDetail {\n field: string\n message: string\n}\n\nexport interface SuccessResponse<T> {\n status: number\n success: true\n data: T\n}\n\nexport interface ErrorResponse {\n status: number\n success: false\n error: string\n message: string\n details?: ValidationDetail[]\n}\n\n// Response Formatters\nexport function formatSuccess<T>(status: number, data: T): SuccessResponse<T> {\n return {\n status,\n success: true,\n data,\n }\n}\n\nexport function formatError(\n status: number,\n error: string,\n message: string,\n details?: ValidationDetail[]\n): ErrorResponse {\n const response: ErrorResponse = {\n status,\n success: false,\n error,\n message,\n }\n if (details && details.length > 0) {\n response.details = details\n }\n return response\n}\n\n// Zod Schema Helpers for Standardized Responses\nexport const successResponseSchema = <T extends z.ZodTypeAny>(dataSchema: T) => z.object({\n status: z.number(),\n success: z.literal(true),\n data: dataSchema,\n})\n\nexport const errorResponseSchema = z.object({\n status: z.number(),\n success: z.literal(false),\n error: z.string(),\n message: z.string(),\n details: z.array(z.object({\n field: z.string(),\n message: z.string(),\n })).optional(),\n})\n","import type { FastifyError, FastifyInstance } from 'fastify'\nimport { formatError, type ValidationDetail } from './response.ts'\n\nexport interface CreateErrorOptions {\n statusCode?: number\n message: string\n name?: string\n}\n\nexport function createError(options: CreateErrorOptions): FastifyError {\n const {\n statusCode = 500,\n message,\n name = 'Error',\n } = options\n\n const error = new Error(message) as FastifyError\n error.statusCode = statusCode\n error.name = name\n return error\n}\n\nexport function setupErrorHandler(fastify: FastifyInstance): void {\n fastify.setErrorHandler((error: FastifyError, request, reply) => {\n // Log all errors\n fastify.log.error({\n err: error,\n url: request.url,\n method: request.method,\n }, 'Request error')\n\n // Handle Zod validation errors\n if (error.validation) {\n const details: ValidationDetail[] = error.validation.map((issue: any) => {\n // Build field path from dataPath or instancePath\n const field = issue.instancePath || issue.dataPath || issue.params?.missingProperty || 'unknown'\n const cleanField = field.startsWith('/') ? field.slice(1).replace(/\\//g, '.') : field\n\n return {\n field: cleanField || 'unknown',\n message: issue.message || 'Validation failed',\n }\n })\n\n return reply.status(400).send(\n formatError(400, 'Validation Error', 'Request validation failed', details)\n )\n }\n\n // Handle rate limit errors\n if (error.statusCode === 429) {\n return reply.status(429).send(\n formatError(429, 'Too Many Requests', 'Rate limit exceeded')\n )\n }\n\n // Handle authentication errors\n if (error.statusCode === 401) {\n return reply.status(401).send(\n formatError(401, 'Unauthorized', error.message || 'Authentication required')\n )\n }\n\n // Handle authorization errors\n if (error.statusCode === 403) {\n return reply.status(403).send(\n formatError(403, 'Forbidden', error.message || 'Access denied')\n )\n }\n\n // Handle not found errors\n if (error.statusCode === 404) {\n return reply.status(404).send(\n formatError(404, 'Not Found', error.message || 'Resource not found')\n )\n }\n\n // Handle all other errors as internal server errors\n const statusCode = error.statusCode || 500\n return reply.status(statusCode).send(\n formatError(\n statusCode,\n statusCode === 500 ? 'Internal Server Error' : error.name || 'Error',\n error.message || 'An unexpected error occurred'\n )\n )\n })\n}\n","import type { FastifyInstance } from 'fastify'\nimport fastifySwagger, { SwaggerOptions } from '@fastify/swagger'\nimport fastifySwaggerUI, { FastifySwaggerUiOptions } from '@fastify/swagger-ui'\nimport { jsonSchemaTransform } from 'fastify-type-provider-zod'\nimport type { CreateFastifyOptions, JWTRouteConfig } from './types.ts'\n\nexport async function setupSwagger(\n fastify: FastifyInstance,\n options: CreateFastifyOptions\n): Promise<string | undefined> {\n if (!options.swagger) {\n return undefined\n }\n\n const openApiConfig: any = {\n openapi: {\n info: {\n title: options.swagger.title,\n version: options.swagger.version,\n description: options.swagger.description,\n },\n },\n transform: jsonSchemaTransform,\n }\n\n // Add bearer auth security scheme if JWT is enabled\n if (options.jwt) {\n openApiConfig.openapi.components = {\n securitySchemes: {\n bearerAuth: {\n type: 'http',\n scheme: 'bearer',\n bearerFormat: 'JWT',\n },\n },\n }\n }\n\n await fastify.register(fastifySwagger, openApiConfig as SwaggerOptions)\n\n let routePrefix = options.swagger.routePrefix || '/docs/'\n\n if (!routePrefix.startsWith('/')) {\n routePrefix = '/' + routePrefix\n }\n\n if (!routePrefix.endsWith('/')) {\n routePrefix = routePrefix + '/'\n }\n\n await fastify.register(fastifySwaggerUI, { routePrefix } as FastifySwaggerUiOptions)\n\n return routePrefix\n}\n\nexport function setupSwaggerSecurityHook(\n fastify: FastifyInstance,\n options: CreateFastifyOptions,\n swaggerRoutePrefix: string | undefined\n): void {\n if (!options.jwt || !options.swagger) {\n return\n }\n\n // Auto-inject security requirements for JWT-protected routes\n fastify.addHook('onRoute', (routeOptions) => {\n // Skip Swagger routes\n if (swaggerRoutePrefix && routeOptions.url.startsWith(swaggerRoutePrefix)) {\n return\n }\n\n const routeConfig = (routeOptions.config as JWTRouteConfig) || {}\n const jwtConfig = routeConfig.jwt\n\n // Skip if JWT is explicitly bypassed\n if (jwtConfig === false) {\n return\n }\n\n // Determine if route requires JWT\n const requiresJWT =\n jwtConfig === true ||\n (typeof jwtConfig === 'object' && jwtConfig.required !== false) ||\n (jwtConfig === undefined && options.jwt?.global === true)\n\n // Inject security requirement if JWT is required\n if (requiresJWT) {\n if (!routeOptions.schema) {\n routeOptions.schema = {}\n }\n if (!routeOptions.schema.security) {\n routeOptions.schema.security = [{ bearerAuth: [] }]\n }\n }\n })\n}\n","import type { FastifyInstance } from 'fastify'\nimport fastifyJwt, { FastifyJWTOptions } from '@fastify/jwt'\nimport { formatError } from './response.ts'\nimport type { CreateFastifyOptions, JWTRouteConfig } from './types.ts'\n\nexport async function setupJWT(\n fastify: FastifyInstance,\n options: CreateFastifyOptions,\n swaggerRoutePrefix: string | undefined\n): Promise<void> {\n if (!options.jwt) {\n return\n }\n\n const jwtOptions: FastifyJWTOptions = {\n secret: options.jwt.secret,\n }\n if (options.jwt.sign !== undefined) {\n jwtOptions.sign = options.jwt.sign\n }\n if (options.jwt.verify !== undefined) {\n jwtOptions.verify = options.jwt.verify\n }\n await fastify.register(fastifyJwt, jwtOptions)\n\n // Global JWT checking hook\n if (options.jwt.global) {\n fastify.addHook('onRequest', async (request, reply) => {\n // Skip JWT verification for Swagger documentation routes\n if (swaggerRoutePrefix && request.url.startsWith(swaggerRoutePrefix)) {\n return\n }\n\n const routeConfig = (request.routeOptions.config as JWTRouteConfig) || {}\n const jwtConfig = routeConfig.jwt\n\n // Check if route explicitly bypasses JWT\n if (jwtConfig === false) {\n return\n }\n\n // Check if route explicitly requires JWT or uses global setting\n const shouldVerify =\n jwtConfig === true ||\n (typeof jwtConfig === 'object' && jwtConfig.required !== false) ||\n (jwtConfig === undefined && options.jwt?.global === true)\n\n if (shouldVerify) {\n try {\n await request.jwtVerify()\n\n // Check if route requires service JWT token\n if (typeof jwtConfig === 'object' && jwtConfig.requireService) {\n const serviceAuthHeader = request.headers['x-service-authorization']\n if (!serviceAuthHeader) {\n return reply.status(401).send(\n formatError(401, 'Unauthorized', 'Missing service authorization token')\n )\n }\n\n const serviceToken = typeof serviceAuthHeader === 'string'\n ? serviceAuthHeader.replace('Bearer ', '')\n : serviceAuthHeader[0]?.replace('Bearer ', '')\n\n if (!serviceToken) {\n return reply.status(401).send(\n formatError(401, 'Unauthorized', 'Invalid service authorization header format')\n )\n }\n\n try {\n const servicePayload = fastify.jwt.verify(serviceToken) as any\n if (servicePayload.type !== 'service') {\n return reply.status(401).send(\n formatError(401, 'Unauthorized', 'Invalid service token type')\n )\n }\n // Store service info in request for potential use\n (request as any).serviceUser = servicePayload\n } catch (err) {\n return reply.status(401).send(\n formatError(401, 'Unauthorized', 'Invalid service authorization token')\n )\n }\n }\n\n // Custom authorization - check JWT info before route processing\n if (typeof jwtConfig === 'object' && jwtConfig.authorize) {\n const authorized = await jwtConfig.authorize(request, reply, request.user)\n if (!authorized) {\n return reply.status(403).send(\n formatError(403, 'Forbidden', 'Authorization failed')\n )\n }\n }\n } catch (err) {\n return reply.status(401).send(\n formatError(401, 'Unauthorized', 'Invalid or missing JWT token')\n )\n }\n }\n })\n }\n}\n","import { z } from 'zod'\nimport { FastifyServer } from './fastify.ts'\n\n/**\n * Base access token payload schema\n */\nexport const baseAccessTokenPayloadSchema = z.object({\n identityId: z.string(),\n type: z.literal('access'),\n exp: z.number(),\n})\n\n/**\n * Base refresh token payload schema\n */\nexport const baseRefreshTokenPayloadSchema = z.object({\n identityId: z.string(),\n type: z.literal('refresh'),\n exp: z.number(),\n})\n\n/**\n * Service-to-service authentication token payload schema\n */\nexport const serviceAccessTokenPayloadSchema = z.object({\n serviceId: z.string(),\n type: z.literal('service'),\n exp: z.number(),\n})\n\n/**\n * Inferred types from base schemas\n */\nexport type BaseAccessTokenPayload = z.infer<typeof baseAccessTokenPayloadSchema>\nexport type BaseRefreshTokenPayload = z.infer<typeof baseRefreshTokenPayloadSchema>\nexport type ServiceAccessTokenPayload = z.infer<typeof serviceAccessTokenPayloadSchema>\n\n/**\n * Token pair interface\n */\nexport interface TokenPair {\n accessToken: string\n refreshToken: string\n}\n\n/**\n * FastifyJwtService configuration options\n */\nexport interface FastifyJwtServiceOptions {\n accessTokenExpiry: string\n refreshTokenExpiry: string\n renewalThreshold: string\n serviceTokenExpiry: string\n accessTokenSchema?: z.ZodSchema\n refreshTokenSchema?: z.ZodSchema\n}\n\n/**\n * Generic JWT service for Fastify applications\n *\n * @template TAccess - Access token payload type extending BaseAccessTokenPayload\n * @template TRefresh - Refresh token payload type extending BaseRefreshTokenPayload\n *\n * @example\n * // Using base schemas\n * const jwtService = new FastifyJwtService(app, {\n * accessTokenExpiry: '15m',\n * refreshTokenExpiry: '7d',\n * renewalThreshold: '1d',\n * })\n *\n * @example\n * // Using extended schemas\n * const customAccessSchema = baseAccessTokenPayloadSchema.extend({\n * roles: z.array(z.string())\n * })\n * const jwtService = new FastifyJwtService(app, {\n * accessTokenExpiry: '15m',\n * refreshTokenExpiry: '7d',\n * renewalThreshold: '1d',\n * accessTokenSchema: customAccessSchema,\n * })\n */\nexport class FastifyJwtService<\n TAccess extends BaseAccessTokenPayload = BaseAccessTokenPayload,\n TRefresh extends BaseRefreshTokenPayload = BaseRefreshTokenPayload\n> {\n private readonly accessTokenExpiry: string\n private readonly refreshTokenExpiry: string\n private readonly renewalThreshold: string\n private readonly serviceTokenExpiry: string\n private readonly accessTokenSchema: z.ZodSchema<TAccess>\n private readonly refreshTokenSchema: z.ZodSchema<TRefresh>\n\n constructor(\n private readonly app: FastifyServer,\n options: FastifyJwtServiceOptions\n ) {\n this.accessTokenExpiry = options.accessTokenExpiry\n this.refreshTokenExpiry = options.refreshTokenExpiry\n this.renewalThreshold = options.renewalThreshold\n this.serviceTokenExpiry = options.serviceTokenExpiry\n this.accessTokenSchema = (options.accessTokenSchema ?? baseAccessTokenPayloadSchema) as z.ZodSchema<TAccess>\n this.refreshTokenSchema = (options.refreshTokenSchema ?? baseRefreshTokenPayloadSchema) as z.ZodSchema<TRefresh>\n }\n\n /**\n * Generate an access token for the given identity\n */\n generateAccessToken(identityId: string, extraPayload?: Partial<Omit<TAccess, 'identityId' | 'type' | 'exp'>>): string {\n const payload = {\n identityId,\n type: 'access' as const,\n ...extraPayload,\n }\n\n return this.app.jwt.sign(payload, {\n expiresIn: this.accessTokenExpiry,\n })\n }\n\n /**\n * Generate a refresh token for the given identity\n */\n generateRefreshToken(identityId: string, extraPayload?: Partial<Omit<TRefresh, 'identityId' | 'type' | 'exp'>>): string {\n const payload = {\n identityId,\n type: 'refresh' as const,\n ...extraPayload,\n }\n\n return this.app.jwt.sign(payload, {\n expiresIn: this.refreshTokenExpiry,\n })\n }\n\n /**\n * Generate both access and refresh tokens\n */\n generateTokenPair(identityId: string): TokenPair {\n return {\n accessToken: this.generateAccessToken(identityId),\n refreshToken: this.generateRefreshToken(identityId),\n }\n }\n\n /**\n * Generate a service-to-service authentication token\n * @param serviceId - The identifier of the service (e.g., 'gateway', 'auth-service')\n * @returns JWT token valid for service-to-service communication\n */\n generateServiceToken(serviceId: string): string {\n const payload = {\n serviceId,\n type: 'service' as const,\n }\n\n return this.app.jwt.sign(payload, {\n expiresIn: this.serviceTokenExpiry,\n })\n }\n\n /**\n * Verify and decode an access token\n */\n verifyAccessToken(token: string): TAccess {\n try {\n const payload = this.app.jwt.verify(token) as unknown\n const validated = this.accessTokenSchema.parse(payload)\n\n if (validated.type !== 'access') {\n throw new Error('Invalid token type')\n }\n\n return validated\n } catch (error) {\n if (error instanceof z.ZodError) {\n throw new Error('Invalid token payload')\n }\n throw error\n }\n }\n\n /**\n * Verify and decode a refresh token\n */\n verifyRefreshToken(token: string): TRefresh {\n try {\n const payload = this.app.jwt.verify(token) as unknown\n const validated = this.refreshTokenSchema.parse(payload)\n\n if (validated.type !== 'refresh') {\n throw new Error('Invalid token type')\n }\n\n return validated\n } catch (error) {\n if (error instanceof z.ZodError) {\n throw new Error('Invalid token payload')\n }\n throw error\n }\n }\n\n /**\n * Renew a refresh token for the given identity\n */\n renewRefreshToken(identityId: string): string {\n return this.generateRefreshToken(identityId)\n }\n\n /**\n * Check if a refresh token should be renewed based on its expiry\n */\n shouldRenewRefreshToken(exp: number): boolean {\n const now = Math.floor(Date.now() / 1000)\n const threshold = this.parseTimespan(this.renewalThreshold)\n return (exp - now) < threshold\n }\n\n /**\n * Parse timespan string to seconds\n */\n private parseTimespan(timespan: string): number {\n const match = timespan.match(/^(\\d+)([smhd])$/)\n if (!match || !match[1] || !match[2]) {\n throw new Error(`Invalid timespan format: ${timespan}`)\n }\n\n const value = parseInt(match[1]!, 10)\n const unit = match[2]!\n\n switch (unit) {\n case 's':\n return value\n case 'm':\n return value * 60\n case 'h':\n return value * 3600\n case 'd':\n return value * 86400\n default:\n throw new Error(`Unknown timespan unit: ${unit}`)\n }\n }\n}\n","import { FastifyPluginCallback } from 'fastify'\nimport { FastifyServer } from './fastify.ts'\nimport { ZodTypeProvider } from 'fastify-type-provider-zod'\n\nexport function createFastifyPlugin(cb: FastifyPluginCallback) {\n return function createFastifyPluginWrapper(\n fastify: FastifyServer,\n options: Parameters<FastifyPluginCallback>[1],\n done: Parameters<FastifyPluginCallback>[2],\n ) {\n const server = fastify.withTypeProvider<ZodTypeProvider>()\n let doneCalled = false\n\n const doneWrapper = (err?: Error) => {\n done(err)\n doneCalled = true\n }\n\n cb(server, options, doneWrapper)\n\n if (!doneCalled) {\n done()\n }\n }\n}\n","import { createFastifyPlugin } from '../plugin.ts'\nimport { FastifyServer } from '../fastify.ts'\n\nexport const healthPlugin = createFastifyPlugin((app: FastifyServer) => {\n app.get('/v1/health', {\n schema: {\n tags: ['health'],\n },\n config: {\n jwt: false,\n },\n handler: async () => {\n return {\n status: 200,\n message: 'ok',\n }\n },\n })\n})\n","export * from './lib/fastify.ts'\nexport * from './lib/response.ts'\nexport * from './lib/types.ts'\nexport * from './lib/jwt.service.ts'\nexport * from './lib/plugin.ts'\nexport * from './lib/plugins/healthPlugin.ts'\nexport * from './lib/error-handler.ts'\nexport * from 'fastify-type-provider-zod'\nexport type { FastifyErrorCodes, FastifyRequest, FastifyReply } from 'fastify'\n"],"mappings":"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","names":["z"]}
|
package/package.json
CHANGED