@engjts/nexus 0.1.8 → 0.1.10

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 (221) hide show
  1. package/dist/advanced/playground/playground.js.map +1 -1
  2. package/dist/advanced/static/generateDirectoryListing.d.ts +1 -1
  3. package/dist/advanced/static/generateDirectoryListing.d.ts.map +1 -1
  4. package/dist/advanced/static/generateDirectoryListing.js +12 -6
  5. package/dist/advanced/static/generateDirectoryListing.js.map +1 -1
  6. package/dist/advanced/static/index.d.ts +2 -0
  7. package/dist/advanced/static/index.d.ts.map +1 -1
  8. package/dist/advanced/static/index.js +4 -1
  9. package/dist/advanced/static/index.js.map +1 -1
  10. package/dist/advanced/static/serveStatic.d.ts.map +1 -1
  11. package/dist/advanced/static/serveStatic.js +7 -1
  12. package/dist/advanced/static/serveStatic.js.map +1 -1
  13. package/dist/index.d.ts +1 -1
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +3 -1
  16. package/dist/index.js.map +1 -1
  17. package/package.json +1 -1
  18. package/BENCHMARK_REPORT.md +0 -343
  19. package/documentation/01-getting-started.md +0 -240
  20. package/documentation/02-context.md +0 -335
  21. package/documentation/03-routing.md +0 -397
  22. package/documentation/04-middleware.md +0 -483
  23. package/documentation/05-validation.md +0 -514
  24. package/documentation/06-error-handling.md +0 -465
  25. package/documentation/07-performance.md +0 -364
  26. package/documentation/08-adapters.md +0 -470
  27. package/documentation/09-api-reference.md +0 -548
  28. package/documentation/10-examples.md +0 -582
  29. package/documentation/11-deployment.md +0 -477
  30. package/documentation/12-sentry.md +0 -620
  31. package/documentation/13-sentry-data-storage.md +0 -996
  32. package/documentation/14-sentry-data-reference.md +0 -457
  33. package/documentation/15-sentry-summary.md +0 -409
  34. package/documentation/16-alerts-system.md +0 -745
  35. package/documentation/17-alert-adapters.md +0 -696
  36. package/documentation/18-alerts-implementation-summary.md +0 -385
  37. package/documentation/19-class-based-routing.md +0 -840
  38. package/documentation/20-websocket-realtime.md +0 -813
  39. package/documentation/21-cache-system.md +0 -510
  40. package/documentation/22-job-queue.md +0 -772
  41. package/documentation/23-sentry-plugin.md +0 -551
  42. package/documentation/24-testing-utilities.md +0 -1287
  43. package/documentation/25-api-versioning.md +0 -533
  44. package/documentation/26-context-store.md +0 -607
  45. package/documentation/27-dependency-injection.md +0 -329
  46. package/documentation/28-lifecycle-hooks.md +0 -521
  47. package/documentation/29-package-structure.md +0 -196
  48. package/documentation/30-plugin-system.md +0 -414
  49. package/documentation/31-jwt-authentication.md +0 -597
  50. package/documentation/32-cli.md +0 -268
  51. package/documentation/ALERTS-COMPLETE-SUMMARY.md +0 -429
  52. package/documentation/ALERTS-INDEX.md +0 -330
  53. package/documentation/ALERTS-QUICK-REFERENCE.md +0 -286
  54. package/documentation/README.md +0 -178
  55. package/documentation/index.html +0 -34
  56. package/modern_framework_paper.md +0 -1870
  57. package/public/css/style.css +0 -87
  58. package/public/index.html +0 -34
  59. package/public/js/app.js +0 -27
  60. package/src/advanced/cache/InMemoryCacheStore.ts +0 -68
  61. package/src/advanced/cache/MultiTierCache.ts +0 -194
  62. package/src/advanced/cache/RedisCacheStore.ts +0 -341
  63. package/src/advanced/cache/index.ts +0 -5
  64. package/src/advanced/cache/types.ts +0 -40
  65. package/src/advanced/graphql/SimpleDataLoader.ts +0 -42
  66. package/src/advanced/graphql/index.ts +0 -22
  67. package/src/advanced/graphql/server.ts +0 -252
  68. package/src/advanced/graphql/types.ts +0 -42
  69. package/src/advanced/jobs/InMemoryQueueStore.ts +0 -68
  70. package/src/advanced/jobs/JobQueue.ts +0 -556
  71. package/src/advanced/jobs/RedisQueueStore.ts +0 -367
  72. package/src/advanced/jobs/index.ts +0 -5
  73. package/src/advanced/jobs/types.ts +0 -70
  74. package/src/advanced/observability/APMManager.ts +0 -163
  75. package/src/advanced/observability/AlertManager.ts +0 -109
  76. package/src/advanced/observability/MetricRegistry.ts +0 -151
  77. package/src/advanced/observability/ObservabilityCenter.ts +0 -304
  78. package/src/advanced/observability/StructuredLogger.ts +0 -154
  79. package/src/advanced/observability/TracingManager.ts +0 -117
  80. package/src/advanced/observability/adapters.ts +0 -304
  81. package/src/advanced/observability/createObservabilityMiddleware.ts +0 -63
  82. package/src/advanced/observability/index.ts +0 -11
  83. package/src/advanced/observability/types.ts +0 -174
  84. package/src/advanced/playground/extractPathParams.ts +0 -6
  85. package/src/advanced/playground/generateFieldExample.ts +0 -31
  86. package/src/advanced/playground/generatePlaygroundHTML.ts +0 -1956
  87. package/src/advanced/playground/generateSummary.ts +0 -19
  88. package/src/advanced/playground/getTagFromPath.ts +0 -9
  89. package/src/advanced/playground/index.ts +0 -8
  90. package/src/advanced/playground/playground.ts +0 -250
  91. package/src/advanced/playground/types.ts +0 -49
  92. package/src/advanced/playground/zodToExample.ts +0 -16
  93. package/src/advanced/playground/zodToParams.ts +0 -15
  94. package/src/advanced/postman/buildAuth.ts +0 -31
  95. package/src/advanced/postman/buildBody.ts +0 -15
  96. package/src/advanced/postman/buildQueryParams.ts +0 -27
  97. package/src/advanced/postman/buildRequestItem.ts +0 -36
  98. package/src/advanced/postman/buildResponses.ts +0 -11
  99. package/src/advanced/postman/buildUrl.ts +0 -33
  100. package/src/advanced/postman/capitalize.ts +0 -4
  101. package/src/advanced/postman/generateCollection.ts +0 -59
  102. package/src/advanced/postman/generateEnvironment.ts +0 -34
  103. package/src/advanced/postman/generateExampleFromZod.ts +0 -21
  104. package/src/advanced/postman/generateFieldExample.ts +0 -45
  105. package/src/advanced/postman/generateName.ts +0 -20
  106. package/src/advanced/postman/generateUUID.ts +0 -11
  107. package/src/advanced/postman/getTagFromPath.ts +0 -10
  108. package/src/advanced/postman/index.ts +0 -28
  109. package/src/advanced/postman/postman.ts +0 -156
  110. package/src/advanced/postman/slugify.ts +0 -7
  111. package/src/advanced/postman/types.ts +0 -140
  112. package/src/advanced/realtime/index.ts +0 -18
  113. package/src/advanced/realtime/websocket.ts +0 -231
  114. package/src/advanced/sentry/index.ts +0 -1236
  115. package/src/advanced/sentry/types.ts +0 -355
  116. package/src/advanced/static/generateDirectoryListing.ts +0 -47
  117. package/src/advanced/static/generateETag.ts +0 -7
  118. package/src/advanced/static/getMimeType.ts +0 -9
  119. package/src/advanced/static/index.ts +0 -32
  120. package/src/advanced/static/isSafePath.ts +0 -13
  121. package/src/advanced/static/publicDir.ts +0 -21
  122. package/src/advanced/static/serveStatic.ts +0 -225
  123. package/src/advanced/static/spa.ts +0 -24
  124. package/src/advanced/static/types.ts +0 -159
  125. package/src/advanced/swagger/SwaggerGenerator.ts +0 -66
  126. package/src/advanced/swagger/buildOperation.ts +0 -61
  127. package/src/advanced/swagger/buildParameters.ts +0 -61
  128. package/src/advanced/swagger/buildRequestBody.ts +0 -21
  129. package/src/advanced/swagger/buildResponses.ts +0 -54
  130. package/src/advanced/swagger/capitalize.ts +0 -5
  131. package/src/advanced/swagger/convertPath.ts +0 -9
  132. package/src/advanced/swagger/createSwagger.ts +0 -12
  133. package/src/advanced/swagger/generateOperationId.ts +0 -21
  134. package/src/advanced/swagger/generateSpec.ts +0 -105
  135. package/src/advanced/swagger/generateSummary.ts +0 -24
  136. package/src/advanced/swagger/generateSwaggerUI.ts +0 -70
  137. package/src/advanced/swagger/generateThemeCss.ts +0 -53
  138. package/src/advanced/swagger/index.ts +0 -25
  139. package/src/advanced/swagger/swagger.ts +0 -237
  140. package/src/advanced/swagger/types.ts +0 -206
  141. package/src/advanced/swagger/zodFieldToOpenAPI.ts +0 -94
  142. package/src/advanced/swagger/zodSchemaToOpenAPI.ts +0 -50
  143. package/src/advanced/swagger/zodToOpenAPI.ts +0 -22
  144. package/src/advanced/testing/factory.ts +0 -509
  145. package/src/advanced/testing/harness.ts +0 -612
  146. package/src/advanced/testing/index.ts +0 -430
  147. package/src/advanced/testing/load-test.ts +0 -618
  148. package/src/advanced/testing/mock-server.ts +0 -498
  149. package/src/advanced/testing/mock.ts +0 -670
  150. package/src/cli/bin.ts +0 -9
  151. package/src/cli/cli.ts +0 -158
  152. package/src/cli/commands/add.ts +0 -178
  153. package/src/cli/commands/build.ts +0 -73
  154. package/src/cli/commands/create.ts +0 -166
  155. package/src/cli/commands/dev.ts +0 -85
  156. package/src/cli/commands/generate.ts +0 -99
  157. package/src/cli/commands/help.ts +0 -95
  158. package/src/cli/commands/init.ts +0 -91
  159. package/src/cli/commands/version.ts +0 -38
  160. package/src/cli/index.ts +0 -6
  161. package/src/cli/templates/generators.ts +0 -359
  162. package/src/cli/templates/index.ts +0 -680
  163. package/src/cli/utils/exec.ts +0 -52
  164. package/src/cli/utils/file-system.ts +0 -78
  165. package/src/cli/utils/logger.ts +0 -111
  166. package/src/core/adapter.ts +0 -88
  167. package/src/core/application.ts +0 -1453
  168. package/src/core/context-pool.ts +0 -79
  169. package/src/core/context.ts +0 -856
  170. package/src/core/index.ts +0 -94
  171. package/src/core/middleware.ts +0 -272
  172. package/src/core/performance/buffer-pool.ts +0 -108
  173. package/src/core/performance/middleware-optimizer.ts +0 -162
  174. package/src/core/plugin/PluginManager.ts +0 -435
  175. package/src/core/plugin/builder.ts +0 -358
  176. package/src/core/plugin/index.ts +0 -50
  177. package/src/core/plugin/types.ts +0 -214
  178. package/src/core/router/file-router.ts +0 -623
  179. package/src/core/router/index.ts +0 -260
  180. package/src/core/router/radix-tree.ts +0 -242
  181. package/src/core/serializer.ts +0 -397
  182. package/src/core/store/index.ts +0 -30
  183. package/src/core/store/registry.ts +0 -178
  184. package/src/core/store/request-store.ts +0 -240
  185. package/src/core/store/types.ts +0 -233
  186. package/src/core/types.ts +0 -616
  187. package/src/database/adapter.ts +0 -35
  188. package/src/database/adapters/index.ts +0 -1
  189. package/src/database/adapters/mysql.ts +0 -669
  190. package/src/database/database.ts +0 -70
  191. package/src/database/dialect.ts +0 -388
  192. package/src/database/index.ts +0 -12
  193. package/src/database/migrations.ts +0 -86
  194. package/src/database/optimizer.ts +0 -125
  195. package/src/database/query-builder.ts +0 -404
  196. package/src/database/realtime.ts +0 -53
  197. package/src/database/schema.ts +0 -71
  198. package/src/database/transactions.ts +0 -56
  199. package/src/database/types.ts +0 -87
  200. package/src/deployment/cluster.ts +0 -471
  201. package/src/deployment/config.ts +0 -454
  202. package/src/deployment/docker.ts +0 -599
  203. package/src/deployment/graceful-shutdown.ts +0 -373
  204. package/src/deployment/index.ts +0 -56
  205. package/src/index.ts +0 -281
  206. package/src/security/adapter.ts +0 -318
  207. package/src/security/auth/JWTPlugin.ts +0 -234
  208. package/src/security/auth/JWTProvider.ts +0 -316
  209. package/src/security/auth/adapter.ts +0 -12
  210. package/src/security/auth/jwt.ts +0 -234
  211. package/src/security/auth/middleware.ts +0 -188
  212. package/src/security/csrf.ts +0 -220
  213. package/src/security/headers.ts +0 -108
  214. package/src/security/index.ts +0 -60
  215. package/src/security/rate-limit/adapter.ts +0 -7
  216. package/src/security/rate-limit/memory.ts +0 -108
  217. package/src/security/rate-limit/middleware.ts +0 -181
  218. package/src/security/sanitization.ts +0 -75
  219. package/src/security/types.ts +0 -240
  220. package/src/security/utils.ts +0 -52
  221. 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