@engjts/nexus 0.1.7 → 0.1.9

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.
Files changed (259) hide show
  1. package/dist/advanced/playground/generatePlaygroundHTML.d.ts.map +1 -1
  2. package/dist/advanced/playground/generatePlaygroundHTML.js +107 -0
  3. package/dist/advanced/playground/generatePlaygroundHTML.js.map +1 -1
  4. package/dist/advanced/playground/playground.d.ts +19 -0
  5. package/dist/advanced/playground/playground.d.ts.map +1 -1
  6. package/dist/advanced/playground/playground.js +70 -0
  7. package/dist/advanced/playground/playground.js.map +1 -1
  8. package/dist/advanced/playground/types.d.ts +20 -0
  9. package/dist/advanced/playground/types.d.ts.map +1 -1
  10. package/dist/core/application.d.ts +14 -0
  11. package/dist/core/application.d.ts.map +1 -1
  12. package/dist/core/application.js +173 -71
  13. package/dist/core/application.js.map +1 -1
  14. package/dist/core/context-pool.d.ts +2 -13
  15. package/dist/core/context-pool.d.ts.map +1 -1
  16. package/dist/core/context-pool.js +7 -45
  17. package/dist/core/context-pool.js.map +1 -1
  18. package/dist/core/context.d.ts +108 -5
  19. package/dist/core/context.d.ts.map +1 -1
  20. package/dist/core/context.js +449 -53
  21. package/dist/core/context.js.map +1 -1
  22. package/dist/core/index.d.ts +1 -0
  23. package/dist/core/index.d.ts.map +1 -1
  24. package/dist/core/index.js +9 -1
  25. package/dist/core/index.js.map +1 -1
  26. package/dist/core/middleware.d.ts +6 -0
  27. package/dist/core/middleware.d.ts.map +1 -1
  28. package/dist/core/middleware.js +83 -84
  29. package/dist/core/middleware.js.map +1 -1
  30. package/dist/core/performance/fast-json.d.ts +149 -0
  31. package/dist/core/performance/fast-json.d.ts.map +1 -0
  32. package/dist/core/performance/fast-json.js +473 -0
  33. package/dist/core/performance/fast-json.js.map +1 -0
  34. package/dist/core/router/file-router.d.ts +20 -7
  35. package/dist/core/router/file-router.d.ts.map +1 -1
  36. package/dist/core/router/file-router.js +41 -13
  37. package/dist/core/router/file-router.js.map +1 -1
  38. package/dist/core/router/index.d.ts +6 -0
  39. package/dist/core/router/index.d.ts.map +1 -1
  40. package/dist/core/router/index.js +33 -6
  41. package/dist/core/router/index.js.map +1 -1
  42. package/dist/core/router/radix-tree.d.ts +4 -1
  43. package/dist/core/router/radix-tree.d.ts.map +1 -1
  44. package/dist/core/router/radix-tree.js +7 -3
  45. package/dist/core/router/radix-tree.js.map +1 -1
  46. package/dist/core/serializer.d.ts +251 -0
  47. package/dist/core/serializer.d.ts.map +1 -0
  48. package/dist/core/serializer.js +290 -0
  49. package/dist/core/serializer.js.map +1 -0
  50. package/dist/core/types.d.ts +39 -1
  51. package/dist/core/types.d.ts.map +1 -1
  52. package/dist/core/types.js.map +1 -1
  53. package/dist/index.d.ts +1 -0
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/index.js +12 -2
  56. package/dist/index.js.map +1 -1
  57. package/package.json +3 -1
  58. package/documentation/01-getting-started.md +0 -240
  59. package/documentation/02-context.md +0 -335
  60. package/documentation/03-routing.md +0 -397
  61. package/documentation/04-middleware.md +0 -483
  62. package/documentation/05-validation.md +0 -514
  63. package/documentation/06-error-handling.md +0 -465
  64. package/documentation/07-performance.md +0 -364
  65. package/documentation/08-adapters.md +0 -470
  66. package/documentation/09-api-reference.md +0 -548
  67. package/documentation/10-examples.md +0 -582
  68. package/documentation/11-deployment.md +0 -477
  69. package/documentation/12-sentry.md +0 -620
  70. package/documentation/13-sentry-data-storage.md +0 -996
  71. package/documentation/14-sentry-data-reference.md +0 -457
  72. package/documentation/15-sentry-summary.md +0 -409
  73. package/documentation/16-alerts-system.md +0 -745
  74. package/documentation/17-alert-adapters.md +0 -696
  75. package/documentation/18-alerts-implementation-summary.md +0 -385
  76. package/documentation/19-class-based-routing.md +0 -840
  77. package/documentation/20-websocket-realtime.md +0 -813
  78. package/documentation/21-cache-system.md +0 -510
  79. package/documentation/22-job-queue.md +0 -772
  80. package/documentation/23-sentry-plugin.md +0 -551
  81. package/documentation/24-testing-utilities.md +0 -1287
  82. package/documentation/25-api-versioning.md +0 -533
  83. package/documentation/26-context-store.md +0 -607
  84. package/documentation/27-dependency-injection.md +0 -329
  85. package/documentation/28-lifecycle-hooks.md +0 -521
  86. package/documentation/29-package-structure.md +0 -196
  87. package/documentation/30-plugin-system.md +0 -414
  88. package/documentation/31-jwt-authentication.md +0 -597
  89. package/documentation/32-cli.md +0 -268
  90. package/documentation/ALERTS-COMPLETE-SUMMARY.md +0 -429
  91. package/documentation/ALERTS-INDEX.md +0 -330
  92. package/documentation/ALERTS-QUICK-REFERENCE.md +0 -286
  93. package/documentation/README.md +0 -178
  94. package/documentation/index.html +0 -34
  95. package/modern_framework_paper.md +0 -1870
  96. package/public/css/style.css +0 -87
  97. package/public/index.html +0 -34
  98. package/public/js/app.js +0 -27
  99. package/src/advanced/cache/InMemoryCacheStore.ts +0 -68
  100. package/src/advanced/cache/MultiTierCache.ts +0 -194
  101. package/src/advanced/cache/RedisCacheStore.ts +0 -341
  102. package/src/advanced/cache/index.ts +0 -5
  103. package/src/advanced/cache/types.ts +0 -40
  104. package/src/advanced/graphql/SimpleDataLoader.ts +0 -42
  105. package/src/advanced/graphql/index.ts +0 -22
  106. package/src/advanced/graphql/server.ts +0 -252
  107. package/src/advanced/graphql/types.ts +0 -42
  108. package/src/advanced/jobs/InMemoryQueueStore.ts +0 -68
  109. package/src/advanced/jobs/JobQueue.ts +0 -556
  110. package/src/advanced/jobs/RedisQueueStore.ts +0 -367
  111. package/src/advanced/jobs/index.ts +0 -5
  112. package/src/advanced/jobs/types.ts +0 -70
  113. package/src/advanced/observability/APMManager.ts +0 -163
  114. package/src/advanced/observability/AlertManager.ts +0 -109
  115. package/src/advanced/observability/MetricRegistry.ts +0 -151
  116. package/src/advanced/observability/ObservabilityCenter.ts +0 -304
  117. package/src/advanced/observability/StructuredLogger.ts +0 -154
  118. package/src/advanced/observability/TracingManager.ts +0 -117
  119. package/src/advanced/observability/adapters.ts +0 -304
  120. package/src/advanced/observability/createObservabilityMiddleware.ts +0 -63
  121. package/src/advanced/observability/index.ts +0 -11
  122. package/src/advanced/observability/types.ts +0 -174
  123. package/src/advanced/playground/extractPathParams.ts +0 -6
  124. package/src/advanced/playground/generateFieldExample.ts +0 -31
  125. package/src/advanced/playground/generatePlaygroundHTML.ts +0 -1849
  126. package/src/advanced/playground/generateSummary.ts +0 -19
  127. package/src/advanced/playground/getTagFromPath.ts +0 -9
  128. package/src/advanced/playground/index.ts +0 -8
  129. package/src/advanced/playground/playground.ts +0 -170
  130. package/src/advanced/playground/types.ts +0 -20
  131. package/src/advanced/playground/zodToExample.ts +0 -16
  132. package/src/advanced/playground/zodToParams.ts +0 -15
  133. package/src/advanced/postman/buildAuth.ts +0 -31
  134. package/src/advanced/postman/buildBody.ts +0 -15
  135. package/src/advanced/postman/buildQueryParams.ts +0 -27
  136. package/src/advanced/postman/buildRequestItem.ts +0 -36
  137. package/src/advanced/postman/buildResponses.ts +0 -11
  138. package/src/advanced/postman/buildUrl.ts +0 -33
  139. package/src/advanced/postman/capitalize.ts +0 -4
  140. package/src/advanced/postman/generateCollection.ts +0 -59
  141. package/src/advanced/postman/generateEnvironment.ts +0 -34
  142. package/src/advanced/postman/generateExampleFromZod.ts +0 -21
  143. package/src/advanced/postman/generateFieldExample.ts +0 -45
  144. package/src/advanced/postman/generateName.ts +0 -20
  145. package/src/advanced/postman/generateUUID.ts +0 -11
  146. package/src/advanced/postman/getTagFromPath.ts +0 -10
  147. package/src/advanced/postman/index.ts +0 -28
  148. package/src/advanced/postman/postman.ts +0 -156
  149. package/src/advanced/postman/slugify.ts +0 -7
  150. package/src/advanced/postman/types.ts +0 -140
  151. package/src/advanced/realtime/index.ts +0 -18
  152. package/src/advanced/realtime/websocket.ts +0 -231
  153. package/src/advanced/sentry/index.ts +0 -1236
  154. package/src/advanced/sentry/types.ts +0 -355
  155. package/src/advanced/static/generateDirectoryListing.ts +0 -47
  156. package/src/advanced/static/generateETag.ts +0 -7
  157. package/src/advanced/static/getMimeType.ts +0 -9
  158. package/src/advanced/static/index.ts +0 -32
  159. package/src/advanced/static/isSafePath.ts +0 -13
  160. package/src/advanced/static/publicDir.ts +0 -21
  161. package/src/advanced/static/serveStatic.ts +0 -225
  162. package/src/advanced/static/spa.ts +0 -24
  163. package/src/advanced/static/types.ts +0 -159
  164. package/src/advanced/swagger/SwaggerGenerator.ts +0 -66
  165. package/src/advanced/swagger/buildOperation.ts +0 -61
  166. package/src/advanced/swagger/buildParameters.ts +0 -61
  167. package/src/advanced/swagger/buildRequestBody.ts +0 -21
  168. package/src/advanced/swagger/buildResponses.ts +0 -54
  169. package/src/advanced/swagger/capitalize.ts +0 -5
  170. package/src/advanced/swagger/convertPath.ts +0 -9
  171. package/src/advanced/swagger/createSwagger.ts +0 -12
  172. package/src/advanced/swagger/generateOperationId.ts +0 -21
  173. package/src/advanced/swagger/generateSpec.ts +0 -105
  174. package/src/advanced/swagger/generateSummary.ts +0 -24
  175. package/src/advanced/swagger/generateSwaggerUI.ts +0 -70
  176. package/src/advanced/swagger/generateThemeCss.ts +0 -53
  177. package/src/advanced/swagger/index.ts +0 -25
  178. package/src/advanced/swagger/swagger.ts +0 -237
  179. package/src/advanced/swagger/types.ts +0 -206
  180. package/src/advanced/swagger/zodFieldToOpenAPI.ts +0 -94
  181. package/src/advanced/swagger/zodSchemaToOpenAPI.ts +0 -50
  182. package/src/advanced/swagger/zodToOpenAPI.ts +0 -22
  183. package/src/advanced/testing/factory.ts +0 -509
  184. package/src/advanced/testing/harness.ts +0 -612
  185. package/src/advanced/testing/index.ts +0 -430
  186. package/src/advanced/testing/load-test.ts +0 -618
  187. package/src/advanced/testing/mock-server.ts +0 -498
  188. package/src/advanced/testing/mock.ts +0 -670
  189. package/src/cli/bin.ts +0 -9
  190. package/src/cli/cli.ts +0 -158
  191. package/src/cli/commands/add.ts +0 -178
  192. package/src/cli/commands/build.ts +0 -73
  193. package/src/cli/commands/create.ts +0 -166
  194. package/src/cli/commands/dev.ts +0 -85
  195. package/src/cli/commands/generate.ts +0 -99
  196. package/src/cli/commands/help.ts +0 -95
  197. package/src/cli/commands/init.ts +0 -91
  198. package/src/cli/commands/version.ts +0 -38
  199. package/src/cli/index.ts +0 -6
  200. package/src/cli/templates/generators.ts +0 -359
  201. package/src/cli/templates/index.ts +0 -680
  202. package/src/cli/utils/exec.ts +0 -52
  203. package/src/cli/utils/file-system.ts +0 -78
  204. package/src/cli/utils/logger.ts +0 -111
  205. package/src/core/adapter.ts +0 -88
  206. package/src/core/application.ts +0 -1335
  207. package/src/core/context-pool.ts +0 -127
  208. package/src/core/context.ts +0 -412
  209. package/src/core/index.ts +0 -80
  210. package/src/core/middleware.ts +0 -262
  211. package/src/core/performance/buffer-pool.ts +0 -108
  212. package/src/core/performance/middleware-optimizer.ts +0 -162
  213. package/src/core/plugin/PluginManager.ts +0 -435
  214. package/src/core/plugin/builder.ts +0 -358
  215. package/src/core/plugin/index.ts +0 -50
  216. package/src/core/plugin/types.ts +0 -214
  217. package/src/core/router/file-router.ts +0 -594
  218. package/src/core/router/index.ts +0 -227
  219. package/src/core/router/radix-tree.ts +0 -226
  220. package/src/core/store/index.ts +0 -30
  221. package/src/core/store/registry.ts +0 -178
  222. package/src/core/store/request-store.ts +0 -240
  223. package/src/core/store/types.ts +0 -233
  224. package/src/core/types.ts +0 -574
  225. package/src/database/adapter.ts +0 -35
  226. package/src/database/adapters/index.ts +0 -1
  227. package/src/database/adapters/mysql.ts +0 -669
  228. package/src/database/database.ts +0 -70
  229. package/src/database/dialect.ts +0 -388
  230. package/src/database/index.ts +0 -12
  231. package/src/database/migrations.ts +0 -86
  232. package/src/database/optimizer.ts +0 -125
  233. package/src/database/query-builder.ts +0 -404
  234. package/src/database/realtime.ts +0 -53
  235. package/src/database/schema.ts +0 -71
  236. package/src/database/transactions.ts +0 -56
  237. package/src/database/types.ts +0 -87
  238. package/src/deployment/cluster.ts +0 -471
  239. package/src/deployment/config.ts +0 -454
  240. package/src/deployment/docker.ts +0 -599
  241. package/src/deployment/graceful-shutdown.ts +0 -373
  242. package/src/deployment/index.ts +0 -56
  243. package/src/index.ts +0 -264
  244. package/src/security/adapter.ts +0 -318
  245. package/src/security/auth/JWTPlugin.ts +0 -234
  246. package/src/security/auth/JWTProvider.ts +0 -316
  247. package/src/security/auth/adapter.ts +0 -12
  248. package/src/security/auth/jwt.ts +0 -234
  249. package/src/security/auth/middleware.ts +0 -188
  250. package/src/security/csrf.ts +0 -220
  251. package/src/security/headers.ts +0 -108
  252. package/src/security/index.ts +0 -60
  253. package/src/security/rate-limit/adapter.ts +0 -7
  254. package/src/security/rate-limit/memory.ts +0 -108
  255. package/src/security/rate-limit/middleware.ts +0 -181
  256. package/src/security/sanitization.ts +0 -75
  257. package/src/security/types.ts +0 -240
  258. package/src/security/utils.ts +0 -52
  259. package/tsconfig.json +0 -39
@@ -1,597 +0,0 @@
1
- # JWT Authentication
2
-
3
- Nexus Framework menyediakan **JWT Provider** dan **JWT Plugin** untuk autentikasi berbasis JSON Web Token yang mudah digunakan, type-safe, dan terintegrasi dengan sistem DI dan Plugin.
4
-
5
- ## Quick Start
6
-
7
- ### Menggunakan JWT Provider (Simple)
8
-
9
- ```typescript
10
- import { createApp } from 'nexus';
11
- import { JWTProvider } from 'nexus/security';
12
-
13
- // 1. Create JWT Provider
14
- const jwt = new JWTProvider({
15
- secret: process.env.JWT_SECRET!,
16
- expiresIn: '7d'
17
- });
18
-
19
- // 2. Inject ke app via DI
20
- const app = createApp().provide({ jwt });
21
-
22
- // 3. Gunakan di route handler
23
- app.post('/api/auth/login', async (ctx, { jwt }) => {
24
- const { email, password } = ctx.body;
25
-
26
- // Validasi credentials...
27
- const user = await validateUser(email, password);
28
-
29
- // Generate token
30
- const token = await jwt.sign({
31
- id: user.id,
32
- email: user.email,
33
- roles: user.roles
34
- });
35
-
36
- return { success: true, token };
37
- });
38
-
39
- // 4. Protected route
40
- app.get('/api/profile', async (ctx, { jwt }) => {
41
- const result = await jwt.verify(ctx);
42
-
43
- if (!result.valid) {
44
- return ctx.response.status(401).json({
45
- error: 'Unauthorized',
46
- message: result.error
47
- });
48
- }
49
-
50
- return { user: result.user };
51
- });
52
-
53
- app.listen(3000);
54
- ```
55
-
56
- ### Menggunakan JWT Plugin (Full Featured)
57
-
58
- ```typescript
59
- import { createApp } from 'nexus';
60
- import { jwtPlugin } from 'nexus/security';
61
-
62
- const app = createApp()
63
- .plugin(jwtPlugin, {
64
- secret: process.env.JWT_SECRET!,
65
- expiresIn: '7d',
66
- autoProtect: true,
67
- publicPaths: ['/api/auth/login', '/api/auth/register', '/health']
68
- });
69
-
70
- await app.initialize();
71
-
72
- // Login route (public)
73
- app.post('/api/auth/login', async (ctx) => {
74
- const jwt = app.getPluginExports('jwt');
75
- const token = await jwt.sign({ id: user.id, email: user.email });
76
- return { token };
77
- });
78
-
79
- // Protected route - user otomatis ada di ctx.user
80
- app.get('/api/profile', async (ctx) => {
81
- return { user: ctx.user };
82
- });
83
-
84
- app.listen(3000);
85
- ```
86
-
87
- ## JWT Provider API
88
-
89
- ### Configuration
90
-
91
- ```typescript
92
- interface JWTProviderConfig {
93
- secret: string; // JWT secret key (wajib)
94
- expiresIn?: string | number; // Token expiry: '1h', '7d', 3600
95
- issuer?: string; // Token issuer (opsional)
96
- audience?: string; // Token audience (opsional)
97
- }
98
- ```
99
-
100
- ### Methods
101
-
102
- #### `sign(payload, options?)`
103
-
104
- Generate JWT token dari payload.
105
-
106
- ```typescript
107
- const token = await jwt.sign({
108
- id: 'user_123',
109
- email: 'john@example.com',
110
- username: 'john',
111
- roles: ['user', 'admin'],
112
- permissions: ['create:posts', 'delete:posts']
113
- });
114
-
115
- // Dengan custom options
116
- const token = await jwt.sign(
117
- { id: user.id },
118
- { expiresIn: '30d' } // Override default expiry
119
- );
120
- ```
121
-
122
- #### `verify(ctx, options?)`
123
-
124
- Verify token dari request context (header/cookie/query).
125
-
126
- ```typescript
127
- const result = await jwt.verify(ctx);
128
-
129
- if (result.valid) {
130
- console.log(result.user); // { id, email, roles, ... }
131
- } else {
132
- console.log(result.error); // 'Token expired', 'Invalid signature', etc.
133
- console.log(result.expired); // true jika token expired
134
- }
135
- ```
136
-
137
- Dengan cookie:
138
-
139
- ```typescript
140
- const result = await jwt.verify(ctx, { cookieName: 'auth_token' });
141
- ```
142
-
143
- #### `verifyToken(token)`
144
-
145
- Verify token string langsung.
146
-
147
- ```typescript
148
- const result = await jwt.verifyToken('eyJhbGciOiJIUzI1NiIs...');
149
- ```
150
-
151
- #### `decode(token)`
152
-
153
- Decode token tanpa verifikasi (untuk debugging).
154
-
155
- ```typescript
156
- const payload = jwt.decode(token);
157
- console.log(payload);
158
- // { id: 'user_123', email: '...', iat: 1234567890, exp: 1234571490 }
159
- ```
160
-
161
- #### `refresh(token, options?)`
162
-
163
- Generate token baru dengan data yang sama tapi expiry baru.
164
-
165
- ```typescript
166
- const newToken = await jwt.refresh(oldToken);
167
-
168
- if (newToken) {
169
- // Token berhasil di-refresh
170
- } else {
171
- // Token invalid, user harus login ulang
172
- }
173
- ```
174
-
175
- #### `middleware(options?)`
176
-
177
- Get middleware untuk protect route.
178
-
179
- ```typescript
180
- // Dengan functional routes
181
- app.get('/protected', jwt.middleware(), async (ctx) => {
182
- return { user: ctx.user };
183
- });
184
- ```
185
-
186
- #### `hasRole(user, role)`
187
-
188
- Check apakah user punya role tertentu.
189
-
190
- ```typescript
191
- if (jwt.hasRole(user, 'admin')) {
192
- // User adalah admin
193
- }
194
-
195
- // Multiple roles (OR)
196
- if (jwt.hasRole(user, ['admin', 'moderator'])) {
197
- // User adalah admin ATAU moderator
198
- }
199
- ```
200
-
201
- #### `hasPermission(user, permission)`
202
-
203
- Check apakah user punya permission tertentu.
204
-
205
- ```typescript
206
- if (jwt.hasPermission(user, 'delete:posts')) {
207
- // User bisa delete posts
208
- }
209
-
210
- // Multiple permissions (OR)
211
- if (jwt.hasPermission(user, ['create:posts', 'edit:posts'])) {
212
- // User bisa create ATAU edit posts
213
- }
214
- ```
215
-
216
- ## JWT Plugin API
217
-
218
- ### Plugin Configuration
219
-
220
- ```typescript
221
- interface JWTPluginConfig {
222
- secret: string; // JWT secret (wajib)
223
- expiresIn?: string | number; // Token expiry
224
- issuer?: string; // Token issuer
225
- audience?: string; // Token audience
226
- autoProtect?: boolean; // Auto-protect semua route (default: false)
227
- publicPaths?: string[]; // Path yang tidak perlu auth
228
- cookieName?: string; // Nama cookie untuk token
229
- onUnauthorized?: (ctx, error) => any; // Custom unauthorized handler
230
- }
231
- ```
232
-
233
- ### Plugin Exports
234
-
235
- ```typescript
236
- interface JWTPluginExports {
237
- provider: JWTProvider;
238
- sign: (payload) => Promise<string>;
239
- verify: (ctx) => Promise<VerifyResult>;
240
- verifyToken: (token) => Promise<VerifyResult>;
241
- decode: (token) => any;
242
- refresh: (token) => Promise<string | null>;
243
- hasRole: (user, role) => boolean;
244
- hasPermission: (user, permission) => boolean;
245
- middleware: () => MiddlewareFunction;
246
- }
247
- ```
248
-
249
- ### Akses Plugin Exports
250
-
251
- ```typescript
252
- // Via app
253
- const jwt = app.getPluginExports<JWTPluginExports>('jwt');
254
- const token = await jwt.sign({ id: user.id });
255
-
256
- // Via context (jika plugin sudah decorate)
257
- app.get('/test', async (ctx) => {
258
- const result = await ctx.jwt.verify(ctx);
259
- return { user: result.user };
260
- });
261
- ```
262
-
263
- ## Penggunaan dengan Class-Based Routes
264
-
265
- ```typescript
266
- import { Route, Context } from 'nexus';
267
- import { JWTProvider } from 'nexus/security';
268
-
269
- // Buat JWT provider global
270
- export const jwt = new JWTProvider({
271
- secret: process.env.JWT_SECRET!,
272
- expiresIn: '1h'
273
- });
274
-
275
- // Login Route
276
- export class LoginRoute extends Route {
277
- pathName = '/api/auth/login';
278
- method = 'POST' as const;
279
-
280
- schema() {
281
- return {
282
- body: z.object({
283
- email: z.string().email(),
284
- password: z.string().min(6)
285
- })
286
- };
287
- }
288
-
289
- async handler(ctx: Context) {
290
- const { email, password } = ctx.body;
291
-
292
- // Validasi credentials...
293
- const user = await this.validateUser(email, password);
294
-
295
- if (!user) {
296
- return ctx.response.status(401).json({
297
- error: 'Invalid credentials'
298
- });
299
- }
300
-
301
- const token = await jwt.sign({
302
- id: user.id,
303
- email: user.email,
304
- roles: user.roles
305
- });
306
-
307
- return { success: true, token };
308
- }
309
-
310
- private async validateUser(email: string, password: string) {
311
- // Implement your validation logic
312
- }
313
- }
314
-
315
- // Protected Route
316
- export class ProfileRoute extends Route {
317
- pathName = '/api/user/profile';
318
- method = 'GET' as const;
319
-
320
- middlewares() {
321
- return [jwt.middleware()];
322
- }
323
-
324
- async handler(ctx: Context) {
325
- const user = (ctx as any).user;
326
- return { user };
327
- }
328
- }
329
-
330
- // Role-Protected Route
331
- export class AdminRoute extends Route {
332
- pathName = '/api/admin/dashboard';
333
- method = 'GET' as const;
334
-
335
- middlewares() {
336
- return [
337
- jwt.middleware(),
338
- // Custom role check middleware
339
- async (ctx: Context, next: any) => {
340
- const user = (ctx as any).user;
341
- if (!jwt.hasRole(user, 'admin')) {
342
- return ctx.response.status(403).json({
343
- error: 'Forbidden',
344
- message: 'Admin access required'
345
- });
346
- }
347
- return next(ctx);
348
- }
349
- ];
350
- }
351
-
352
- async handler(ctx: Context) {
353
- return { message: 'Welcome to admin dashboard!' };
354
- }
355
- }
356
- ```
357
-
358
- ## Token Expiry Format
359
-
360
- ```typescript
361
- // Detik
362
- expiresIn: 3600 // 1 jam
363
-
364
- // String format
365
- expiresIn: '30s' // 30 detik
366
- expiresIn: '15m' // 15 menit
367
- expiresIn: '1h' // 1 jam
368
- expiresIn: '7d' // 7 hari
369
- ```
370
-
371
- ## Token Extraction
372
-
373
- JWT Provider otomatis mengekstrak token dari:
374
-
375
- 1. **Authorization Header** (prioritas pertama)
376
- ```
377
- Authorization: Bearer <token>
378
- ```
379
-
380
- 2. **Cookie** (jika `cookieName` di-set)
381
- ```
382
- Cookie: auth_token=<token>
383
- ```
384
-
385
- 3. **Query Parameter** (untuk WebSocket atau special cases)
386
- ```
387
- GET /api/profile?token=<token>
388
- ```
389
-
390
- ## Auto-Protect Mode (Plugin)
391
-
392
- Dengan `autoProtect: true`, semua route otomatis dilindungi kecuali yang ada di `publicPaths`:
393
-
394
- ```typescript
395
- app.plugin(jwtPlugin, {
396
- secret: process.env.JWT_SECRET!,
397
- autoProtect: true,
398
- publicPaths: [
399
- '/health',
400
- '/api/auth/login',
401
- '/api/auth/register',
402
- '/api/public/*' // Wildcard support
403
- ]
404
- });
405
- ```
406
-
407
- ## Custom Unauthorized Handler
408
-
409
- ```typescript
410
- app.plugin(jwtPlugin, {
411
- secret: process.env.JWT_SECRET!,
412
- autoProtect: true,
413
- onUnauthorized: (ctx, error) => {
414
- // Custom response
415
- return ctx.response.status(401).json({
416
- success: false,
417
- error: 'Authentication Required',
418
- message: error,
419
- loginUrl: '/api/auth/login'
420
- });
421
- }
422
- });
423
- ```
424
-
425
- ## Verify Result Type
426
-
427
- ```typescript
428
- interface VerifyResult {
429
- valid: boolean; // true jika token valid
430
- user: User | null; // User data dari token
431
- error?: string; // Error message jika invalid
432
- expired?: boolean; // true jika token expired
433
- }
434
-
435
- interface User {
436
- id: string | number;
437
- email?: string;
438
- username?: string;
439
- roles?: string[];
440
- permissions?: string[];
441
- }
442
- ```
443
-
444
- ## Best Practices
445
-
446
- ### 1. Gunakan Environment Variable untuk Secret
447
-
448
- ```typescript
449
- // ❌ Jangan hardcode
450
- const jwt = new JWTProvider({ secret: 'my-secret' });
451
-
452
- // ✅ Gunakan env variable
453
- const jwt = new JWTProvider({
454
- secret: process.env.JWT_SECRET!
455
- });
456
- ```
457
-
458
- ### 2. Secret Minimal 32 Karakter
459
-
460
- ```bash
461
- # Generate random secret
462
- openssl rand -base64 32
463
- ```
464
-
465
- ### 3. Gunakan Expiry yang Sesuai
466
-
467
- ```typescript
468
- // Access token: pendek (1 jam)
469
- const accessToken = await jwt.sign(user, { expiresIn: '1h' });
470
-
471
- // Refresh token: panjang (7 hari)
472
- const refreshToken = await jwt.sign(
473
- { id: user.id, type: 'refresh' },
474
- { expiresIn: '7d' }
475
- );
476
- ```
477
-
478
- ### 4. Handle Token Expired
479
-
480
- ```typescript
481
- app.get('/api/data', async (ctx, { jwt }) => {
482
- const result = await jwt.verify(ctx);
483
-
484
- if (!result.valid) {
485
- if (result.expired) {
486
- return ctx.response.status(401).json({
487
- error: 'Token Expired',
488
- code: 'TOKEN_EXPIRED' // Client bisa refresh token
489
- });
490
- }
491
- return ctx.response.status(401).json({
492
- error: 'Invalid Token',
493
- code: 'INVALID_TOKEN' // Client harus login ulang
494
- });
495
- }
496
-
497
- return { data: 'secret data' };
498
- });
499
- ```
500
-
501
- ### 5. Implement Refresh Token Flow
502
-
503
- ```typescript
504
- // Endpoint untuk refresh token
505
- app.post('/api/auth/refresh', async (ctx, { jwt }) => {
506
- const { refreshToken } = ctx.body;
507
-
508
- const result = await jwt.verifyToken(refreshToken);
509
-
510
- if (!result.valid || result.user?.type !== 'refresh') {
511
- return ctx.response.status(401).json({
512
- error: 'Invalid refresh token'
513
- });
514
- }
515
-
516
- // Generate new access token
517
- const accessToken = await jwt.sign({
518
- id: result.user.id,
519
- email: result.user.email,
520
- roles: result.user.roles
521
- }, { expiresIn: '1h' });
522
-
523
- return { accessToken };
524
- });
525
- ```
526
-
527
- ## Testing
528
-
529
- ```typescript
530
- import { TestClient } from 'nexus/testing';
531
-
532
- describe('JWT Authentication', () => {
533
- const jwt = new JWTProvider({ secret: 'test-secret-min-32-characters!!' });
534
-
535
- test('should generate valid token', async () => {
536
- const token = await jwt.sign({ id: 1, email: 'test@test.com' });
537
- expect(token).toBeDefined();
538
- expect(token.split('.')).toHaveLength(3);
539
- });
540
-
541
- test('should verify valid token', async () => {
542
- const token = await jwt.sign({ id: 1, email: 'test@test.com' });
543
- const result = await jwt.verifyToken(token);
544
-
545
- expect(result.valid).toBe(true);
546
- expect(result.user?.id).toBe(1);
547
- expect(result.user?.email).toBe('test@test.com');
548
- });
549
-
550
- test('should reject expired token', async () => {
551
- const token = await jwt.sign(
552
- { id: 1 },
553
- { expiresIn: '1s' }
554
- );
555
-
556
- // Wait for token to expire
557
- await new Promise(r => setTimeout(r, 1100));
558
-
559
- const result = await jwt.verifyToken(token);
560
- expect(result.valid).toBe(false);
561
- expect(result.expired).toBe(true);
562
- });
563
-
564
- test('protected route should require token', async () => {
565
- const app = createApp().provide({ jwt });
566
-
567
- app.get('/protected', async (ctx, { jwt }) => {
568
- const result = await jwt.verify(ctx);
569
- if (!result.valid) {
570
- return ctx.response.status(401).json({ error: 'Unauthorized' });
571
- }
572
- return { user: result.user };
573
- });
574
-
575
- const client = new TestClient(app);
576
-
577
- // Without token
578
- const res1 = await client.get('/protected');
579
- expect(res1.status).toBe(401);
580
-
581
- // With valid token
582
- const token = await jwt.sign({ id: 1, email: 'test@test.com' });
583
- const res2 = await client.get('/protected', {
584
- headers: { Authorization: `Bearer ${token}` }
585
- });
586
- expect(res2.status).toBe(200);
587
- expect(res2.body.user.id).toBe(1);
588
- });
589
- });
590
- ```
591
-
592
- ## See Also
593
-
594
- - [Dependency Injection](./27-dependency-injection.md) - DI system untuk inject JWT Provider
595
- - [Plugin System](./30-plugin-system.md) - Plugin system untuk JWT Plugin
596
- - [Class-Based Routing](./19-class-based-routing.md) - Penggunaan dengan class routes
597
- - [Middleware](./04-middleware.md) - Custom middleware