@xenterprises/fastify-xauth-jwks 1.0.0
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/AUTHENTICATION_EXAMPLE.md +453 -0
- package/CACHING.md +282 -0
- package/CONFIGURATION.md +545 -0
- package/DEVELOPMENT.md +385 -0
- package/JOSE_UTILITIES.md +204 -0
- package/KEYS_GENERATION.md +359 -0
- package/QUICK_START.md +334 -0
- package/README.md +73 -0
- package/package.json +44 -0
- package/server/app.js +370 -0
- package/server/example-jwks.json +12 -0
- package/server/generate-demo-token.js +232 -0
- package/src/index.js +9 -0
- package/src/services/pathValidator.js +175 -0
- package/src/utils/index.js +145 -0
- package/src/xAuth.js +36 -0
- package/test/integration.test.js +259 -0
- package/test/utils.test.js +195 -0
- package/test/xAuth.test.js +439 -0
package/README.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# xAuthJWSK
|
|
2
|
+
|
|
3
|
+
**Lightweight, zero-config path-based JWT/JWKS validation for Fastify v5.**
|
|
4
|
+
|
|
5
|
+
Protect multiple API paths with independent JWKS providers. Simple, fast, and production-ready.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
✅ **Path-Based Protection** - Protect `/admin`, `/portal`, `/api` with different JWKS providers
|
|
10
|
+
✅ **Bearer Token Validation** - Automatic JWT validation against remote JWKS endpoints
|
|
11
|
+
✅ **Local or Remote JWKS** - Use remote URLs for production or local JWKS data for development
|
|
12
|
+
✅ **Dual-Level Caching** - JWKS cache (30 min) + JWT payload cache (5 min)
|
|
13
|
+
✅ **Excluded Paths** - Skip auth for health checks, docs, etc.
|
|
14
|
+
✅ **Zero Dependencies** - Only uses `jose` and `fastify-plugin`
|
|
15
|
+
✅ **Slim & Focused** - ~200 lines of core code, no bloat
|
|
16
|
+
✅ **Configurable** - All caching parameters customizable
|
|
17
|
+
✅ **Request Isolation** - Each path has separate validator & cache
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install @xenterprises/fastify-xauth-jwks
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Quick Example
|
|
26
|
+
|
|
27
|
+
```javascript
|
|
28
|
+
import Fastify from 'fastify';
|
|
29
|
+
import xAuthJWSK from '@xenterprises/fastify-xauth-jwks';
|
|
30
|
+
|
|
31
|
+
const fastify = Fastify();
|
|
32
|
+
|
|
33
|
+
await fastify.register(xAuthJWSK, {
|
|
34
|
+
paths: {
|
|
35
|
+
admin: {
|
|
36
|
+
pathPattern: "/admin",
|
|
37
|
+
jwksUrl: "https://your-auth.com/.well-known/jwks.json",
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
fastify.get('/admin/users', (request) => {
|
|
43
|
+
return { userId: request.auth.userId };
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
fastify.listen({ port: 3000 });
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Documentation
|
|
50
|
+
|
|
51
|
+
**Getting Started:**
|
|
52
|
+
- **[QUICK_START.md](./QUICK_START.md)** - Get started in 5 minutes
|
|
53
|
+
- **[CONFIGURATION.md](./CONFIGURATION.md)** - Complete configuration reference (all options + examples)
|
|
54
|
+
|
|
55
|
+
**Examples & Guides:**
|
|
56
|
+
- **[AUTHENTICATION_EXAMPLE.md](./AUTHENTICATION_EXAMPLE.md)** - Email/password auth with JWT signing
|
|
57
|
+
- **[DEVELOPMENT.md](./DEVELOPMENT.md)** - Local development with test tokens
|
|
58
|
+
- **[KEYS_GENERATION.md](./KEYS_GENERATION.md)** - Generate JWKS keys and test tokens
|
|
59
|
+
|
|
60
|
+
**Advanced:**
|
|
61
|
+
- **[CACHING.md](./CACHING.md)** - Configure caching for performance
|
|
62
|
+
- **[JOSE_UTILITIES.md](./JOSE_UTILITIES.md)** - Advanced JWT inspection
|
|
63
|
+
|
|
64
|
+
## Tests
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
npm test
|
|
68
|
+
# 49/49 tests passing ✅
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## License
|
|
72
|
+
|
|
73
|
+
ISC
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@xenterprises/fastify-xauth-jwks",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"description": "Fastify plugin for path-based JWT/JWKS validation. Protect multiple paths with independent JWKS providers.",
|
|
6
|
+
"main": "src/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/index.js",
|
|
9
|
+
"./utils": "./src/utils/index.js"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"start": "fastify start -l info server/app.js",
|
|
13
|
+
"dev": "fastify start -w -l info -P server/app.js",
|
|
14
|
+
"test": "node --test test/xAuth.test.js test/utils.test.js test/integration.test.js"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"fastify",
|
|
18
|
+
"jwt",
|
|
19
|
+
"jwks",
|
|
20
|
+
"jose",
|
|
21
|
+
"validation",
|
|
22
|
+
"path-based"
|
|
23
|
+
],
|
|
24
|
+
"author": "Tim Mushen",
|
|
25
|
+
"license": "ISC",
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "https://github.com/xenterprises/fastify-xauth-jwks"
|
|
29
|
+
},
|
|
30
|
+
"bugs": {
|
|
31
|
+
"url": "https://github.com/xenterprises/fastify-xauth-jwks/issues"
|
|
32
|
+
},
|
|
33
|
+
"homepage": "https://github.com/xenterprises/fastify-xauth-jwks#readme",
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"fastify-plugin": "^5.0.0",
|
|
36
|
+
"jose": "^5.9.6"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"fastify": "^5.1.0"
|
|
40
|
+
},
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"fastify": "^5.0.0"
|
|
43
|
+
}
|
|
44
|
+
}
|
package/server/app.js
ADDED
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* xAuthJWSK Demo Server
|
|
3
|
+
*
|
|
4
|
+
* Shows path-based JWT/JWKS validation with flexible configuration:
|
|
5
|
+
* - Development: Local JWKS data (example-jwks.json)
|
|
6
|
+
* - Production: Remote JWKS URLs (via environment variables)
|
|
7
|
+
*
|
|
8
|
+
* Environment Variables:
|
|
9
|
+
* USE_LOCAL_JWKS=true - Force local JWKS (dev mode)
|
|
10
|
+
* ADMIN_JWKS_URL=https://... - Admin path JWKS endpoint
|
|
11
|
+
* PORTAL_JWKS_URL=https://... - Portal path JWKS endpoint
|
|
12
|
+
* PARTNER_JWKS_URL=https://... - Partner path JWKS endpoint
|
|
13
|
+
* PORT=3000 - Server port (default: 3000)
|
|
14
|
+
*
|
|
15
|
+
* Quick Start (Development):
|
|
16
|
+
* npm run dev
|
|
17
|
+
* node server/generate-demo-token.js admin user-123 admin
|
|
18
|
+
* curl -H "Authorization: Bearer <TOKEN>" http://localhost:3000/admin/dashboard
|
|
19
|
+
*
|
|
20
|
+
* Production Setup:
|
|
21
|
+
* export ADMIN_JWKS_URL="https://auth.example.com/.well-known/jwks.json"
|
|
22
|
+
* export PORTAL_JWKS_URL="https://auth.example.com/.well-known/jwks.json"
|
|
23
|
+
* npm start
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import Fastify from "fastify";
|
|
27
|
+
import xAuthJWSK from "../src/xAuth.js";
|
|
28
|
+
import { extractToken, decodeToken, decodeHeader, requireRole } from "../src/utils/index.js";
|
|
29
|
+
import exampleJwks from "./example-jwks.json" assert { type: "json" };
|
|
30
|
+
|
|
31
|
+
const fastify = Fastify({
|
|
32
|
+
logger: true,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// ============================================================================
|
|
36
|
+
// Configuration Strategy
|
|
37
|
+
// ============================================================================
|
|
38
|
+
// Flexible configuration for both development and production:
|
|
39
|
+
// - Development: Use local JWKS (example-jwks.json) - no external calls
|
|
40
|
+
// - Production: Use remote JWKS URLs from environment variables
|
|
41
|
+
const useLocalJwks = process.env.USE_LOCAL_JWKS === "true" || !process.env.ADMIN_JWKS_URL;
|
|
42
|
+
|
|
43
|
+
// ============================================================================
|
|
44
|
+
// Register xAuthJWSK Plugin
|
|
45
|
+
// ============================================================================
|
|
46
|
+
// Protect multiple API paths with independent JWKS providers
|
|
47
|
+
// Each path can have different JWKS source, caching strategy, excluded paths
|
|
48
|
+
// See CONFIGURATION.md for complete reference
|
|
49
|
+
await fastify.register(xAuthJWSK, {
|
|
50
|
+
paths: {
|
|
51
|
+
// ========================================================================
|
|
52
|
+
// ADMIN API - Strict authentication (e.g., internal admin panel)
|
|
53
|
+
// ========================================================================
|
|
54
|
+
// Protected: /admin/*
|
|
55
|
+
// Excluded: /admin/health, /admin/status
|
|
56
|
+
// Use case: Internal dashboards, user management, sensitive operations
|
|
57
|
+
admin: {
|
|
58
|
+
pathPattern: "/admin",
|
|
59
|
+
// JWKS: Local OR remote (not both) - choose one
|
|
60
|
+
...(useLocalJwks
|
|
61
|
+
? { jwksData: exampleJwks } // Development: local file
|
|
62
|
+
: { jwksUrl: process.env.ADMIN_JWKS_URL || "https://example.com/admin/.well-known/jwks.json" }), // Production: env var
|
|
63
|
+
// Paths that don't require authentication (health checks, etc)
|
|
64
|
+
excludedPaths: ["/health", "/status"],
|
|
65
|
+
// Caching: Strict cache - refresh every 5 minutes
|
|
66
|
+
enablePayloadCache: true,
|
|
67
|
+
payloadCacheTTL: 300000, // 5 min token payload cache
|
|
68
|
+
jwksCacheMaxAge: 1800000, // 30 min JWKS cache (production keys rarely change)
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
// ========================================================================
|
|
72
|
+
// PORTAL API - User-facing (e.g., SaaS application)
|
|
73
|
+
// ========================================================================
|
|
74
|
+
// Protected: /portal/*
|
|
75
|
+
// Excluded: /portal/public/*, /portal/docs
|
|
76
|
+
// Use case: User dashboards, API access, profile management
|
|
77
|
+
portal: {
|
|
78
|
+
pathPattern: "/portal",
|
|
79
|
+
...(useLocalJwks
|
|
80
|
+
? { jwksData: exampleJwks }
|
|
81
|
+
: { jwksUrl: process.env.PORTAL_JWKS_URL || "https://example.com/portal/.well-known/jwks.json" }),
|
|
82
|
+
// Public endpoints (marketing pages, documentation)
|
|
83
|
+
excludedPaths: ["/public", "/docs"],
|
|
84
|
+
// Caching: Standard cache - balance performance and freshness
|
|
85
|
+
enablePayloadCache: true,
|
|
86
|
+
payloadCacheTTL: 300000, // 5 min
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
// ========================================================================
|
|
90
|
+
// PARTNER API - Integration endpoint (e.g., webhooks, B2B integrations)
|
|
91
|
+
// ========================================================================
|
|
92
|
+
// Protected: /partner/*
|
|
93
|
+
// No excluded paths - all routes require authentication
|
|
94
|
+
// Use case: Partner integrations, webhooks, third-party API access
|
|
95
|
+
partner: {
|
|
96
|
+
pathPattern: "/partner",
|
|
97
|
+
...(useLocalJwks
|
|
98
|
+
? { jwksData: exampleJwks }
|
|
99
|
+
: { jwksUrl: process.env.PARTNER_JWKS_URL || "https://example.com/partner/.well-known/jwks.json" }),
|
|
100
|
+
// All routes require authentication - no exclusions
|
|
101
|
+
// Longer cache TTL - partner tokens typically valid longer
|
|
102
|
+
enablePayloadCache: true,
|
|
103
|
+
payloadCacheTTL: 600000, // 10 min - partners have longer-lived tokens
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// ============================================================================
|
|
109
|
+
// Public Routes (No Authentication Required)
|
|
110
|
+
// ============================================================================
|
|
111
|
+
|
|
112
|
+
fastify.get("/", async () => {
|
|
113
|
+
return {
|
|
114
|
+
service: "xAuthJWSK Demo",
|
|
115
|
+
version: "1.0.0",
|
|
116
|
+
protectedPaths: Object.keys(fastify.xAuth.validators),
|
|
117
|
+
docs: "https://github.com/xenterprises/fastify-xauth-jwks",
|
|
118
|
+
};
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
fastify.get("/health", async () => {
|
|
122
|
+
return { status: "ok", timestamp: new Date().toISOString() };
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// ============================================================================
|
|
126
|
+
// Admin Routes (Protected by /admin)
|
|
127
|
+
// ============================================================================
|
|
128
|
+
|
|
129
|
+
fastify.get("/admin/health", async () => {
|
|
130
|
+
return { status: "healthy", service: "admin-api" };
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
fastify.get("/admin/status", async () => {
|
|
134
|
+
return { status: "operational", uptime: process.uptime() };
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
fastify.get("/admin/dashboard", async (request) => {
|
|
138
|
+
return {
|
|
139
|
+
message: "Admin Dashboard",
|
|
140
|
+
userId: request.auth.userId,
|
|
141
|
+
user: request.auth.payload,
|
|
142
|
+
authenticatedVia: request.auth.path,
|
|
143
|
+
};
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
fastify.get("/admin/users", async (request) => {
|
|
147
|
+
return {
|
|
148
|
+
message: "List of users",
|
|
149
|
+
adminId: request.auth.userId,
|
|
150
|
+
users: [
|
|
151
|
+
{ id: "user_1", name: "John Doe", email: "john@example.com" },
|
|
152
|
+
{ id: "user_2", name: "Jane Smith", email: "jane@example.com" },
|
|
153
|
+
],
|
|
154
|
+
};
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
fastify.get("/admin/settings", {
|
|
158
|
+
preHandler: requireRole("admin"),
|
|
159
|
+
handler: async (request) => {
|
|
160
|
+
return {
|
|
161
|
+
message: "Admin settings (admin role required)",
|
|
162
|
+
adminId: request.auth.userId,
|
|
163
|
+
roles: request.user?.roles || [],
|
|
164
|
+
};
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// Cache management endpoint
|
|
169
|
+
fastify.post("/admin/cache/clear", async (request) => {
|
|
170
|
+
const validator = fastify.xAuth.validators.admin;
|
|
171
|
+
validator.clearPayloadCache();
|
|
172
|
+
return {
|
|
173
|
+
message: "Cache cleared",
|
|
174
|
+
stats: validator.getPayloadCacheStats(),
|
|
175
|
+
};
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
fastify.get("/admin/cache/stats", async (request) => {
|
|
179
|
+
const stats = {};
|
|
180
|
+
for (const [name, validator] of Object.entries(fastify.xAuth.validators)) {
|
|
181
|
+
stats[name] = validator.getPayloadCacheStats();
|
|
182
|
+
}
|
|
183
|
+
return { cacheStats: stats };
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// ============================================================================
|
|
187
|
+
// Portal Routes (Protected by /portal)
|
|
188
|
+
// ============================================================================
|
|
189
|
+
|
|
190
|
+
fastify.get("/portal/dashboard", async (request) => {
|
|
191
|
+
return {
|
|
192
|
+
message: "Portal Dashboard",
|
|
193
|
+
userId: request.auth.userId,
|
|
194
|
+
user: request.auth.payload,
|
|
195
|
+
};
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
fastify.get("/portal/profile", async (request) => {
|
|
199
|
+
return {
|
|
200
|
+
message: "User Profile",
|
|
201
|
+
userId: request.auth.userId,
|
|
202
|
+
email: request.user?.email,
|
|
203
|
+
name: request.user?.name,
|
|
204
|
+
};
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
fastify.get("/portal/public/docs", async () => {
|
|
208
|
+
return {
|
|
209
|
+
message: "API Documentation",
|
|
210
|
+
endpoints: ["GET /portal/dashboard", "GET /portal/profile", "POST /portal/update"],
|
|
211
|
+
};
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
fastify.get("/portal/public/pricing", async () => {
|
|
215
|
+
return {
|
|
216
|
+
plans: [
|
|
217
|
+
{ tier: "free", price: 0 },
|
|
218
|
+
{ tier: "pro", price: 29 },
|
|
219
|
+
{ tier: "enterprise", price: 299 },
|
|
220
|
+
],
|
|
221
|
+
};
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
// ============================================================================
|
|
225
|
+
// Partner Routes (Protected by /partner)
|
|
226
|
+
// ============================================================================
|
|
227
|
+
|
|
228
|
+
fastify.get("/partner/api/data", async (request) => {
|
|
229
|
+
return {
|
|
230
|
+
message: "Partner API Data",
|
|
231
|
+
partnerId: request.auth.userId,
|
|
232
|
+
data: { status: "connected", lastSync: new Date().toISOString() },
|
|
233
|
+
};
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
fastify.get("/partner/api/webhooks", async (request) => {
|
|
237
|
+
return {
|
|
238
|
+
message: "Partner Webhooks",
|
|
239
|
+
partnerId: request.auth.userId,
|
|
240
|
+
webhooks: [
|
|
241
|
+
{ id: "wh_1", event: "user.created", url: "https://partner.example.com/webhooks" },
|
|
242
|
+
],
|
|
243
|
+
};
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
// ============================================================================
|
|
247
|
+
// Token Inspection Endpoints (No Auth Required - for debugging)
|
|
248
|
+
// ============================================================================
|
|
249
|
+
|
|
250
|
+
fastify.post("/debug/decode-token", async (request, reply) => {
|
|
251
|
+
const { token } = request.body;
|
|
252
|
+
|
|
253
|
+
if (!token) {
|
|
254
|
+
return reply.code(400).send({ error: "Token required" });
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
try {
|
|
258
|
+
const header = decodeHeader(token);
|
|
259
|
+
const payload = decodeToken(token);
|
|
260
|
+
|
|
261
|
+
return {
|
|
262
|
+
header,
|
|
263
|
+
payload,
|
|
264
|
+
warning: "⚠️ This is UNVERIFIED token data. Only for debugging. Do NOT trust in production.",
|
|
265
|
+
};
|
|
266
|
+
} catch (error) {
|
|
267
|
+
return reply.code(400).send({ error: "Invalid token format" });
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
fastify.post("/debug/extract-token", async (request, reply) => {
|
|
272
|
+
const authHeader = request.headers.authorization;
|
|
273
|
+
|
|
274
|
+
if (!authHeader) {
|
|
275
|
+
return reply.code(400).send({ error: "Authorization header required" });
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const token = extractToken(request);
|
|
279
|
+
|
|
280
|
+
if (!token) {
|
|
281
|
+
return reply.code(400).send({ error: "Invalid Bearer token format" });
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
try {
|
|
285
|
+
const payload = decodeToken(token);
|
|
286
|
+
return {
|
|
287
|
+
extracted: true,
|
|
288
|
+
userId: payload.sub,
|
|
289
|
+
expiresAt: new Date(payload.exp * 1000).toISOString(),
|
|
290
|
+
payload,
|
|
291
|
+
};
|
|
292
|
+
} catch (error) {
|
|
293
|
+
return reply.code(400).send({ error: "Failed to decode token" });
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
// ============================================================================
|
|
298
|
+
// Validator Inspection Endpoint
|
|
299
|
+
// ============================================================================
|
|
300
|
+
|
|
301
|
+
fastify.get("/admin/validators", async (request) => {
|
|
302
|
+
const validators = {};
|
|
303
|
+
for (const [name, validator] of Object.entries(fastify.xAuth.validators)) {
|
|
304
|
+
validators[name] = {
|
|
305
|
+
name: validator.name,
|
|
306
|
+
pathPattern: validator.pathPattern,
|
|
307
|
+
jwksUrl: validator.jwksUrl,
|
|
308
|
+
config: validator.config,
|
|
309
|
+
cacheStats: validator.getPayloadCacheStats(),
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
return { validators };
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
// ============================================================================
|
|
316
|
+
// Server Startup
|
|
317
|
+
// ============================================================================
|
|
318
|
+
|
|
319
|
+
const start = async () => {
|
|
320
|
+
try {
|
|
321
|
+
await fastify.listen({ port: process.env.PORT || 3000, host: "0.0.0.0" });
|
|
322
|
+
|
|
323
|
+
console.log("\n🚀 xAuthJWSK Demo Server Started!\n");
|
|
324
|
+
console.log(`Mode: ${useLocalJwks ? "🏠 LOCAL (Development)" : "☁️ REMOTE (Production)"}`);
|
|
325
|
+
if (useLocalJwks) {
|
|
326
|
+
console.log(" Using example-jwks.json - perfect for testing!");
|
|
327
|
+
console.log(" Generate test tokens: node server/generate-demo-token.js [path] [userId] [role]");
|
|
328
|
+
}
|
|
329
|
+
console.log("\nProtected Paths:");
|
|
330
|
+
for (const [name, validator] of Object.entries(fastify.xAuth.validators)) {
|
|
331
|
+
console.log(`\n 📍 ${name.toUpperCase()}`);
|
|
332
|
+
console.log(` Path: ${validator.pathPattern}`);
|
|
333
|
+
console.log(` JWKS: ${useLocalJwks ? "local (example-jwks.json)" : validator.jwksUrl}`);
|
|
334
|
+
console.log(` Cache: ${validator.config.enablePayloadCache ? "enabled" : "disabled"}`);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
console.log("\n\nPublic Endpoints:");
|
|
338
|
+
console.log(" GET / - API info");
|
|
339
|
+
console.log(" GET /health - Health check");
|
|
340
|
+
console.log(" POST /debug/decode-token - Decode JWT (debug only)");
|
|
341
|
+
console.log(" POST /debug/extract-token - Extract token from header");
|
|
342
|
+
console.log(" GET /admin/validators - View validator configs");
|
|
343
|
+
|
|
344
|
+
console.log("\n\nProtected Endpoints (require Bearer token):");
|
|
345
|
+
console.log(" GET /admin/dashboard - Admin dashboard");
|
|
346
|
+
console.log(" GET /admin/users - List users");
|
|
347
|
+
console.log(" GET /admin/settings - Settings (requires 'admin' role)");
|
|
348
|
+
console.log(" POST /admin/cache/clear - Clear auth cache");
|
|
349
|
+
console.log(" GET /admin/cache/stats - View cache statistics");
|
|
350
|
+
console.log(" GET /portal/dashboard - Portal dashboard");
|
|
351
|
+
console.log(" GET /portal/profile - User profile");
|
|
352
|
+
console.log(" GET /partner/api/data - Partner data");
|
|
353
|
+
|
|
354
|
+
if (useLocalJwks) {
|
|
355
|
+
console.log("\n\n🧪 Testing with Local JWKS:");
|
|
356
|
+
console.log(" 1. Generate a test token:");
|
|
357
|
+
console.log(" node server/generate-demo-token.js admin user-123 admin");
|
|
358
|
+
console.log(" 2. Copy the token and test a protected endpoint:");
|
|
359
|
+
console.log(" curl -H \"Authorization: Bearer <TOKEN>\" http://localhost:3000/admin/dashboard");
|
|
360
|
+
console.log(" 3. For more info, see KEYS_GENERATION.md and QUICK_START.md");
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
console.log("\n💡 Tip: Add '-H \"Authorization: Bearer YOUR_TOKEN\"' to protected requests\n");
|
|
364
|
+
} catch (err) {
|
|
365
|
+
fastify.log.error(err);
|
|
366
|
+
process.exit(1);
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
start();
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"keys": [
|
|
3
|
+
{
|
|
4
|
+
"kty": "RSA",
|
|
5
|
+
"use": "sig",
|
|
6
|
+
"alg": "RS256",
|
|
7
|
+
"kid": "demo-key-admin",
|
|
8
|
+
"n": "xjlCRBqkQWeBpaMWV2E2h6L1zcqmxm0W3Z5BbMwP9jfYEJ_ZHvMdV8fYaWDV8xzGqL7Z9fQaL7bXmVzYcPz0Xq5L_VmE8V7K0L1M2N3O4P5Q6R7S8T9U0V1W2X3Y4Z5A6B7C8D9E0F1G2H3I4J5K6L7M8N9O0P1Q2R3S4T5U6V7W8X9Y0Z1A2B3C4D5E6F7G8H9I0J1K2L3M4N5O6P7Q8R9S0T1U2V3W4X5Y6Z7A8B9C0D1E2F3G4H5I6J7K8L9M0N1O2P3Q4R5S6T7U8V9W0X1Y2Z3A4B5C6D7E8F9G0H1I2J3K4L5M6N7O8P9Q0R1S2T3U4V5W6X7Y8Z9",
|
|
9
|
+
"e": "AQAB"
|
|
10
|
+
}
|
|
11
|
+
]
|
|
12
|
+
}
|