@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
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Generate demo JWT tokens for testing xAuthJWSK
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* node generate-demo-token.js [pathName] [userId] [role]
|
|
8
|
+
*
|
|
9
|
+
* Examples:
|
|
10
|
+
* node generate-demo-token.js admin user-123 admin
|
|
11
|
+
* node generate-demo-token.js portal user-456 user
|
|
12
|
+
* node generate-demo-token.js partner partner-789
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import * as jose from 'jose';
|
|
16
|
+
|
|
17
|
+
// Demo JWKS keys - these are public keys only
|
|
18
|
+
// In production, private keys would be stored securely
|
|
19
|
+
const demoKeys = {
|
|
20
|
+
admin: {
|
|
21
|
+
kid: 'demo-key-admin',
|
|
22
|
+
// This is a demo key for testing ONLY - never use in production
|
|
23
|
+
privateKeyPem: `-----BEGIN PRIVATE KEY-----
|
|
24
|
+
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDGOUJEGqRBZ4Gl
|
|
25
|
+
oxZXYTaHovXNyqbGbRbdnkFszA/2N9gQn9ke8x1Xx9hpYNXzHMaovtn19BovttmZ
|
|
26
|
+
XNhw/PRerkv9WYTxXsrQnUjczUjczVTNzNT0xM01M1MzQzM1M9T1M9M1TzU0MzU0
|
|
27
|
+
M1MzQzMzQ0M1MzMzM1M1QzMzMzM1RDMzM1M1QzMzM1M1QzMzM1M1M1MzMzM1M1Mz
|
|
28
|
+
MzM1M1MzMzM1M1MzMzMzUzMzMzM1M1MzMzMzUzMzMzM1M1MzMzMzUzMzMzM1M1Mz
|
|
29
|
+
MzMzUzMzMzM1M1MzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMz
|
|
30
|
+
MzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMz
|
|
31
|
+
MzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMz
|
|
32
|
+
AgMBAAECggEAQQECDQwVFBcWGRgbHBwdHR4eHx8gICEhISIiIiIjIyMjIyMkJCQk
|
|
33
|
+
JCQlJSUlJSUlJSYmJiYmJiYmJiYnJycnJycnJycnJycnJycnJycnJycnJycnJycnJ
|
|
34
|
+
yckJycnJycnJycoJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJyc
|
|
35
|
+
nJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
36
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
37
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
38
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
39
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
40
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
41
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
42
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
43
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
44
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
45
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
46
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
47
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
48
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
49
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
50
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
51
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
52
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
53
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
54
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
55
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
56
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
57
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
58
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
59
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
60
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
61
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
62
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
63
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
64
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
65
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
66
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
67
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
68
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
69
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
70
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
71
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
72
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
73
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
74
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
75
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
76
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
77
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
78
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
79
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
80
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
81
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
82
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
83
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
84
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
85
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
86
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
87
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
88
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
89
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
90
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
91
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
92
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
93
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
94
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
95
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
96
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
97
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
98
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
99
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
100
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
101
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
102
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
103
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
104
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
105
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
106
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
107
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
108
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
109
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
110
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
111
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
112
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
113
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
114
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
115
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
116
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
117
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
118
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
119
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
120
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
121
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
122
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
123
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
124
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
125
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
126
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
127
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
128
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
129
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
130
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
131
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
132
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
133
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
134
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
135
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
136
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
137
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
138
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
139
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
140
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
141
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
142
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
143
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
144
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
145
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
146
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
147
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
148
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
149
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
150
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
151
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
152
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
153
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
154
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
155
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
156
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
157
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
158
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
159
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
160
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
161
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
162
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
163
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
164
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
165
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
166
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
167
|
+
cnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJy
|
|
168
|
+
-----END PRIVATE KEY-----`,
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
async function generateToken() {
|
|
173
|
+
const args = process.argv.slice(2);
|
|
174
|
+
const pathName = args[0] || 'admin';
|
|
175
|
+
const userId = args[1] || 'user-' + Math.random().toString(36).substring(7);
|
|
176
|
+
const role = args[2] || 'user';
|
|
177
|
+
|
|
178
|
+
const keyConfig = demoKeys[pathName];
|
|
179
|
+
if (!keyConfig) {
|
|
180
|
+
console.error(`❌ No demo key for path: ${pathName}`);
|
|
181
|
+
console.error(` Available paths: ${Object.keys(demoKeys).join(', ')}`);
|
|
182
|
+
process.exit(1);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
try {
|
|
186
|
+
// Import private key
|
|
187
|
+
const privateKey = await jose.importPKCS8(
|
|
188
|
+
keyConfig.privateKeyPem,
|
|
189
|
+
'RS256'
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
// Create JWT
|
|
193
|
+
const token = await new jose.SignJWT({
|
|
194
|
+
sub: userId,
|
|
195
|
+
userId: userId,
|
|
196
|
+
name: `Test User (${pathName})`,
|
|
197
|
+
email: `${userId}@example.com`,
|
|
198
|
+
roles: [role],
|
|
199
|
+
iat: Math.floor(Date.now() / 1000),
|
|
200
|
+
exp: Math.floor(Date.now() / 1000) + 3600, // 1 hour
|
|
201
|
+
})
|
|
202
|
+
.setProtectedHeader({
|
|
203
|
+
alg: 'RS256',
|
|
204
|
+
kid: `demo-key-${pathName}`,
|
|
205
|
+
typ: 'JWT',
|
|
206
|
+
})
|
|
207
|
+
.sign(privateKey);
|
|
208
|
+
|
|
209
|
+
console.log('🎫 Generated Test Token:\n');
|
|
210
|
+
console.log(token);
|
|
211
|
+
console.log('\n📋 Token Details:');
|
|
212
|
+
console.log(` Path: ${pathName}`);
|
|
213
|
+
console.log(` User ID: ${userId}`);
|
|
214
|
+
console.log(` Role: ${role}`);
|
|
215
|
+
console.log(` Expires: 1 hour`);
|
|
216
|
+
console.log('\n🔗 Usage Examples:\n');
|
|
217
|
+
console.log('# cURL:');
|
|
218
|
+
console.log(`curl -H "Authorization: Bearer ${token}" \\`);
|
|
219
|
+
console.log(` http://localhost:3000/${pathName}/dashboard\n`);
|
|
220
|
+
console.log('# JavaScript fetch:');
|
|
221
|
+
console.log(`fetch('http://localhost:3000/${pathName}/dashboard', {`);
|
|
222
|
+
console.log(` headers: { Authorization: 'Bearer ${token}' }`);
|
|
223
|
+
console.log(`})\n`);
|
|
224
|
+
console.log('# Decode token (for inspection):');
|
|
225
|
+
console.log(`node -e "console.log(JSON.stringify(JSON.parse(Buffer.from('${token}'.split('.')[1], 'base64').toString()), null, 2))"\n`);
|
|
226
|
+
} catch (error) {
|
|
227
|
+
console.error('❌ Error generating token:', error.message);
|
|
228
|
+
process.exit(1);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
generateToken();
|
package/src/index.js
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
// src/services/pathValidator.js
|
|
2
|
+
import * as jose from "jose";
|
|
3
|
+
import { extractToken } from "../utils/index.js";
|
|
4
|
+
|
|
5
|
+
export async function createPathValidator(fastify, pathName, config) {
|
|
6
|
+
const {
|
|
7
|
+
pathPattern = `/${pathName}`,
|
|
8
|
+
jwksUrl,
|
|
9
|
+
jwksData,
|
|
10
|
+
excludedPaths = [],
|
|
11
|
+
// JWKS caching configuration
|
|
12
|
+
jwksCooldownDuration = 30000, // 30 seconds between refetches
|
|
13
|
+
jwksCacheMaxAge = 1800000, // 30 minutes cache duration
|
|
14
|
+
// JWT payload caching configuration
|
|
15
|
+
enablePayloadCache = true,
|
|
16
|
+
payloadCacheTTL = 300000, // 5 minutes default
|
|
17
|
+
} = config;
|
|
18
|
+
|
|
19
|
+
// Validate configuration
|
|
20
|
+
if (!jwksUrl && !jwksData) {
|
|
21
|
+
throw new Error(`${pathName}: Either jwksUrl or jwksData is required`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (jwksUrl && jwksData) {
|
|
25
|
+
throw new Error(`${pathName}: Cannot specify both jwksUrl and jwksData`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Create JWKS for token verification
|
|
29
|
+
let jwks;
|
|
30
|
+
if (jwksUrl) {
|
|
31
|
+
// Remote JWKS endpoint
|
|
32
|
+
jwks = jose.createRemoteJWKSet(new URL(jwksUrl), {
|
|
33
|
+
cooldownDuration: jwksCooldownDuration,
|
|
34
|
+
cacheMaxAge: jwksCacheMaxAge,
|
|
35
|
+
});
|
|
36
|
+
} else {
|
|
37
|
+
// Local JWKS data - can be either:
|
|
38
|
+
// 1. Full JWKS object: { keys: [...] }
|
|
39
|
+
// 2. Single JWK key: { kty: "RSA", ... } (will be wrapped in JWKS format)
|
|
40
|
+
const jwksFormatted = jwksData.keys ? jwksData : { keys: [jwksData] };
|
|
41
|
+
jwks = jose.createLocalJWKSet(jwksFormatted);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// JWT payload cache (token -> {payload, expiresAt})
|
|
45
|
+
const payloadCache = new Map();
|
|
46
|
+
|
|
47
|
+
// Build full excluded paths
|
|
48
|
+
const fullExcludedPaths = excludedPaths.map((path) => {
|
|
49
|
+
if (path.startsWith("/")) {
|
|
50
|
+
return `${pathPattern}${path}`;
|
|
51
|
+
}
|
|
52
|
+
return `${pathPattern}/${path}`;
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// JWT verification function with optional payload caching
|
|
56
|
+
async function verifyJWT(accessToken) {
|
|
57
|
+
try {
|
|
58
|
+
if (!accessToken || typeof accessToken !== "string") {
|
|
59
|
+
fastify.log.error(`[${pathName}] Invalid token format`);
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Check payload cache first if enabled
|
|
64
|
+
if (enablePayloadCache) {
|
|
65
|
+
const cached = payloadCache.get(accessToken);
|
|
66
|
+
if (cached && cached.expiresAt > Date.now()) {
|
|
67
|
+
return cached.payload;
|
|
68
|
+
} else if (cached) {
|
|
69
|
+
// Expired, remove from cache
|
|
70
|
+
payloadCache.delete(accessToken);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const { payload } = await jose.jwtVerify(accessToken, jwks);
|
|
75
|
+
|
|
76
|
+
if (!payload || !payload.sub) {
|
|
77
|
+
fastify.log.error(`[${pathName}] Invalid token payload`);
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Cache the payload if enabled
|
|
82
|
+
if (enablePayloadCache) {
|
|
83
|
+
payloadCache.set(accessToken, {
|
|
84
|
+
payload,
|
|
85
|
+
expiresAt: Date.now() + payloadCacheTTL,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return payload;
|
|
90
|
+
} catch (error) {
|
|
91
|
+
fastify.log.error(`[${pathName}] Token verification failed:`, error.message);
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Cache cleanup function (optional, for manual cache management)
|
|
97
|
+
function clearPayloadCache() {
|
|
98
|
+
payloadCache.clear();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Get cache stats (for monitoring)
|
|
102
|
+
function getPayloadCacheStats() {
|
|
103
|
+
return {
|
|
104
|
+
size: payloadCache.size,
|
|
105
|
+
enabled: enablePayloadCache,
|
|
106
|
+
ttl: payloadCacheTTL,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Authentication middleware
|
|
111
|
+
fastify.addHook("onRequest", async (request, reply) => {
|
|
112
|
+
const url = request.url;
|
|
113
|
+
|
|
114
|
+
// Only protect paths matching this pattern
|
|
115
|
+
if (!url.startsWith(pathPattern)) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Skip excluded paths
|
|
120
|
+
if (fullExcludedPaths.some((path) => url.startsWith(path))) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
// Get token from header
|
|
126
|
+
const token = extractToken(request);
|
|
127
|
+
if (!token) {
|
|
128
|
+
return reply.code(401).send({
|
|
129
|
+
error: "Access token required",
|
|
130
|
+
path: pathName,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Verify token
|
|
135
|
+
const payload = await verifyJWT(token);
|
|
136
|
+
|
|
137
|
+
if (!payload) {
|
|
138
|
+
return reply.code(401).send({
|
|
139
|
+
error: "Invalid token",
|
|
140
|
+
path: pathName,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Set user info on request
|
|
145
|
+
request.user = payload;
|
|
146
|
+
request.auth = {
|
|
147
|
+
path: pathName,
|
|
148
|
+
userId: payload.sub,
|
|
149
|
+
payload,
|
|
150
|
+
};
|
|
151
|
+
} catch (err) {
|
|
152
|
+
fastify.log.error(`[${pathName}] Authentication failed:`, err);
|
|
153
|
+
return reply.code(401).send({
|
|
154
|
+
error: "Authentication failed",
|
|
155
|
+
path: pathName,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// Return validator info and methods
|
|
161
|
+
return {
|
|
162
|
+
name: pathName,
|
|
163
|
+
pathPattern,
|
|
164
|
+
jwksUrl,
|
|
165
|
+
verifyJWT,
|
|
166
|
+
clearPayloadCache,
|
|
167
|
+
getPayloadCacheStats,
|
|
168
|
+
config: {
|
|
169
|
+
jwksCooldownDuration,
|
|
170
|
+
jwksCacheMaxAge,
|
|
171
|
+
enablePayloadCache,
|
|
172
|
+
payloadCacheTTL,
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
// src/utils/index.js
|
|
2
|
+
import { decodeJwt, decodeProtectedHeader } from "jose";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Extract JWT token from Authorization header
|
|
6
|
+
* @param {object} request - Fastify request object
|
|
7
|
+
* @returns {string|null} - JWT token or null
|
|
8
|
+
*/
|
|
9
|
+
export function extractToken(request) {
|
|
10
|
+
const authHeader = request.headers.authorization;
|
|
11
|
+
|
|
12
|
+
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return authHeader.slice(7);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Check if user has specific role
|
|
21
|
+
* @param {object} user - User payload from JWT
|
|
22
|
+
* @param {string|Array<string>} roles - Role(s) to check
|
|
23
|
+
* @returns {boolean}
|
|
24
|
+
*/
|
|
25
|
+
export function hasRole(user, roles) {
|
|
26
|
+
if (!user || !user.roles) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const userRoles = Array.isArray(user.roles) ? user.roles : [user.roles];
|
|
31
|
+
const requiredRoles = Array.isArray(roles) ? roles : [roles];
|
|
32
|
+
|
|
33
|
+
return requiredRoles.some((role) => userRoles.includes(role));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Check if user has specific permission
|
|
38
|
+
* @param {object} user - User payload from JWT
|
|
39
|
+
* @param {string|Array<string>} permissions - Permission(s) to check
|
|
40
|
+
* @returns {boolean}
|
|
41
|
+
*/
|
|
42
|
+
export function hasPermission(user, permissions) {
|
|
43
|
+
if (!user || !user.permissions) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const userPermissions = Array.isArray(user.permissions)
|
|
48
|
+
? user.permissions
|
|
49
|
+
: [user.permissions];
|
|
50
|
+
const requiredPermissions = Array.isArray(permissions) ? permissions : [permissions];
|
|
51
|
+
|
|
52
|
+
return requiredPermissions.some((permission) => userPermissions.includes(permission));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Get user ID from request
|
|
57
|
+
* @param {object} request - Fastify request object
|
|
58
|
+
* @returns {string|null}
|
|
59
|
+
*/
|
|
60
|
+
export function getUserId(request) {
|
|
61
|
+
return request.auth?.userId || request.user?.sub || null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Get auth endpoint name from request
|
|
66
|
+
* @param {object} request - Fastify request object
|
|
67
|
+
* @returns {string|null}
|
|
68
|
+
*/
|
|
69
|
+
export function getAuthEndpoint(request) {
|
|
70
|
+
return request.auth?.endpoint || null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Create role check decorator for Fastify routes
|
|
75
|
+
* @param {string|Array<string>} roles - Required role(s)
|
|
76
|
+
* @returns {Function} - Fastify preHandler hook
|
|
77
|
+
*/
|
|
78
|
+
export function requireRole(roles) {
|
|
79
|
+
return async function (request, reply) {
|
|
80
|
+
if (!hasRole(request.user, roles)) {
|
|
81
|
+
return reply.code(403).send({
|
|
82
|
+
error: "Forbidden",
|
|
83
|
+
message: "Insufficient permissions",
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Create permission check decorator for Fastify routes
|
|
91
|
+
* @param {string|Array<string>} permissions - Required permission(s)
|
|
92
|
+
* @returns {Function} - Fastify preHandler hook
|
|
93
|
+
*/
|
|
94
|
+
export function requirePermission(permissions) {
|
|
95
|
+
return async function (request, reply) {
|
|
96
|
+
if (!hasPermission(request.user, permissions)) {
|
|
97
|
+
return reply.code(403).send({
|
|
98
|
+
error: "Forbidden",
|
|
99
|
+
message: "Insufficient permissions",
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Create endpoint check decorator for Fastify routes
|
|
107
|
+
* @param {string} endpointName - Required endpoint name
|
|
108
|
+
* @returns {Function} - Fastify preHandler hook
|
|
109
|
+
*/
|
|
110
|
+
export function requireEndpoint(endpointName) {
|
|
111
|
+
return async function (request, reply) {
|
|
112
|
+
if (getAuthEndpoint(request) !== endpointName) {
|
|
113
|
+
return reply.code(403).send({
|
|
114
|
+
error: "Forbidden",
|
|
115
|
+
message: `Must authenticate via ${endpointName} endpoint`,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ============================================================================
|
|
122
|
+
// Re-exported jose utilities for advanced use cases
|
|
123
|
+
// ============================================================================
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Decode JWT payload without verification
|
|
127
|
+
* Useful for inspecting token claims before processing
|
|
128
|
+
* @param {string} token - JWT token
|
|
129
|
+
* @returns {object} - Decoded payload
|
|
130
|
+
* @example
|
|
131
|
+
* const payload = decodeToken('eyJ...');
|
|
132
|
+
* console.log(payload.sub, payload.exp);
|
|
133
|
+
*/
|
|
134
|
+
export { decodeJwt as decodeToken };
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Decode JWT header without verification
|
|
138
|
+
* Useful for inspecting algorithm and key ID
|
|
139
|
+
* @param {string} token - JWT token
|
|
140
|
+
* @returns {object} - Decoded header
|
|
141
|
+
* @example
|
|
142
|
+
* const header = decodeHeader('eyJ...');
|
|
143
|
+
* console.log(header.alg, header.kid);
|
|
144
|
+
*/
|
|
145
|
+
export { decodeProtectedHeader as decodeHeader };
|
package/src/xAuth.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// src/xAuth.js
|
|
2
|
+
import fp from "fastify-plugin";
|
|
3
|
+
import { createPathValidator } from "./services/pathValidator.js";
|
|
4
|
+
|
|
5
|
+
async function xAuthPlugin(fastify, options) {
|
|
6
|
+
const { paths = {} } = options;
|
|
7
|
+
|
|
8
|
+
if (Object.keys(paths).length === 0) {
|
|
9
|
+
throw new Error("At least one protected path configuration is required");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Create xAuth namespace
|
|
13
|
+
const xAuth = {
|
|
14
|
+
validators: {},
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// Setup validator for each path
|
|
18
|
+
for (const [pathName, pathConfig] of Object.entries(paths)) {
|
|
19
|
+
if (pathConfig.active === false) {
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Create path validator with JWKS
|
|
24
|
+
const validator = await createPathValidator(fastify, pathName, pathConfig);
|
|
25
|
+
|
|
26
|
+
// Store validator in xAuth namespace
|
|
27
|
+
xAuth.validators[pathName] = validator;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Decorate Fastify with xAuth namespace
|
|
31
|
+
fastify.decorate("xAuth", xAuth);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export default fp(xAuthPlugin, {
|
|
35
|
+
name: "xAuth",
|
|
36
|
+
});
|