@zola_do/authorization 0.2.5 → 0.2.6

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 (2) hide show
  1. package/README.md +491 -90
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,6 +1,23 @@
1
1
  # @zola_do/authorization
2
2
 
3
- JWT authentication, API key validation, guards, and strategies for NestJS.
3
+ [![npm version](https://img.shields.io/npm/v/@zola_do/authorization.svg)](https://www.npmjs.com/package/@zola_do/authorization)
4
+ [![npm downloads](https://img.shields.io/npm/dm/@zola_do/authorization.svg)](https://www.npmjs.com/package/@zola_do/authorization)
5
+ [![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](https://opensource.org/licenses/ISC)
6
+
7
+ JWT authentication, API key validation, guards, and strategies for NestJS applications.
8
+
9
+ ## Overview
10
+
11
+ `@zola_do/authorization` provides a complete authentication and authorization solution:
12
+
13
+ - **JWT Guards** — Bearer token authentication
14
+ - **Permission Guards** — Role-based access control (RBAC)
15
+ - **API Key Guard** — External API authentication
16
+ - **Strategies** — Passport JWT strategies (access + refresh tokens)
17
+ - **Decorators** — `@CurrentUser()`, `@AllowAnonymous()`
18
+ - **Auth Helper** — Token generation, password hashing, OTP
19
+
20
+ By default, `JwtGuard` is registered as a global guard, protecting all routes.
4
21
 
5
22
  ## Installation
6
23
 
@@ -12,31 +29,36 @@ npm install @zola_do/authorization
12
29
  npm install @zola_do/nestjs-shared
13
30
  ```
14
31
 
15
- ## Recommended Imports
16
-
17
- `@zola_do/authorization` root imports remain fully supported for backward compatibility.
18
- For new code, prefer focused subpath imports:
32
+ ### Dependencies
19
33
 
20
- ```typescript
21
- import { AuthorizationModule } from '@zola_do/authorization/module';
22
- import { JwtGuard, PermissionsGuard } from '@zola_do/authorization/guards';
23
- import { AllowAnonymous, CurrentUser } from '@zola_do/authorization/decorators';
24
- import { AuthHelper } from '@zola_do/authorization/helper';
34
+ ```bash
35
+ npm install @nestjs/jwt @nestjs/passport passport passport-jwt bcrypt jsonwebtoken
25
36
  ```
26
37
 
27
- Optional throttler guard stays available at:
38
+ ### Optional Dependencies
28
39
 
29
- ```typescript
30
- import { ThrottlerBehindProxyGuard } from '@zola_do/authorization/optional/throttler';
40
+ ```bash
41
+ # For ThrottlerBehindProxyGuard
42
+ npm install @nestjs/throttler
31
43
  ```
32
44
 
33
- ## Usage
45
+ ## Quick Start
34
46
 
35
- ### Module Setup
47
+ ### 1. Configure Environment
48
+
49
+ ```bash
50
+ # .env
51
+ JWT_ACCESS_TOKEN_SECRET=your-access-token-secret-min-32-chars
52
+ JWT_ACCESS_TOKEN_EXPIRES=15m
53
+ JWT_REFRESH_TOKEN_SECRET=your-refresh-token-secret-min-32-chars
54
+ JWT_REFRESH_TOKEN_EXPIRES=7d
55
+ ```
56
+
57
+ ### 2. Register Module
36
58
 
37
59
  ```typescript
38
- import { Module } from '@nestjs/common';
39
- import { AuthorizationModule } from '@zola_do/authorization';
60
+ import { Module } from "@nestjs/common";
61
+ import { AuthorizationModule } from "@zola_do/authorization";
40
62
 
41
63
  @Module({
42
64
  imports: [AuthorizationModule],
@@ -44,147 +66,526 @@ import { AuthorizationModule } from '@zola_do/authorization';
44
66
  export class AppModule {}
45
67
  ```
46
68
 
47
- By default, `JwtGuard` is registered as a global guard. Protect routes automatically; use `@AllowAnonymous()` for public routes.
69
+ ### 3. Access User in Controller
70
+
71
+ ```typescript
72
+ import { Controller, Get } from "@nestjs/common";
73
+ import { CurrentUser, JwtGuard, UseGuards } from "@zola_do/authorization";
74
+
75
+ @Controller("profile")
76
+ export class ProfileController {
77
+ @Get()
78
+ @UseGuards(JwtGuard) // Optional - global guard is active by default
79
+ getProfile(@CurrentUser() user: any) {
80
+ return {
81
+ id: user.id,
82
+ email: user.email,
83
+ organization: user.organization,
84
+ };
85
+ }
86
+ }
87
+ ```
88
+
89
+ ## JWT Flow
90
+
91
+ ```
92
+ ┌─────────────────────────────────────────────────────────────────────┐
93
+ │ Login Flow │
94
+ ├─────────────────────────────────────────────────────────────────────┤
95
+ │ │
96
+ │ Client Backend JWT Payload │
97
+ │ │ │ │ │
98
+ │ │ POST /login │ │ │
99
+ │ │ {email,pass} │ │ │
100
+ │ │───────────────>│ │ │
101
+ │ │ │ │ │
102
+ │ │ ┌─────▼─────┐ │ │
103
+ │ │ │ Validate │ │ │
104
+ │ │ │ credentials│ │ │
105
+ │ │ └─────┬─────┘ │ │
106
+ │ │ │ │ │
107
+ │ │ ┌─────▼───────────────┐ │ │
108
+ │ │ │ Generate tokens: │ │ │
109
+ │ │ │ - accessToken │─────────────┼─────────────────>│
110
+ │ │ │ - refreshToken │ │ { │
111
+ │ │ └────────────────────┘ │ sub: userId, │
112
+ │ │ │ │ email, │
113
+ │ │ │ │ organization, │
114
+ │ │ │ │ permissions │
115
+ │ │<───────────────│ │ } │
116
+ │ │ { accessToken,│ │
117
+ │ │ refreshToken│ │
118
+ │ │ } │ │
119
+ │ │ │ │
120
+ └────┼────────────────┼───────────────────────────────────────────────┘
121
+ │ │
122
+ │ │
123
+ ▼ ▼
124
+ ┌─────────────────────────────────────────────────────────────────────┐
125
+ │ Authenticated Request │
126
+ ├─────────────────────────────────────────────────────────────────────┤
127
+ │ │
128
+ │ Client Backend JwtGuard │
129
+ │ │ │ │ │
130
+ │ │ GET /profile │ │ │
131
+ │ │ Authorization:│ │ │
132
+ │ │ Bearer <token>│ │ │
133
+ │ │───────────────>│ │ │
134
+ │ │ │ │ │
135
+ │ │ │ ┌────▼─────┐ │ │
136
+ │ │ │ │ Extract │ │ │
137
+ │ │ │ │ token │ │ │
138
+ │ │ │ └────┬─────┘ │ │
139
+ │ │ │ │ │ │
140
+ │ │ │ ┌────▼────────────┐ │ │
141
+ │ │ │ │ Verify with │ │ │
142
+ │ │ │ │ JWT_ACCESS_ │ │ │
143
+ │ │ │ │ TOKEN_SECRET │ │ │
144
+ │ │ │ └────┬────────────┘ │ │
145
+ │ │ │ │ │ │
146
+ │ │ │ │ set request.user │ │
147
+ │ │ │ ▼ │ │
148
+ │ │ │ req.user = payload │ │
149
+ │ │ │ │ │
150
+ │ │<───────────────│ Controller runs │ │
151
+ │ │ { user data } │ @CurrentUser() = user │ │
152
+ │ │ │ │ │
153
+ └────┴────────────────┴───────────────────────────────────────────────┘
154
+ ```
155
+
156
+ ## Guards
157
+
158
+ ### JwtGuard
159
+
160
+ Default global guard that validates Bearer tokens:
161
+
162
+ ```typescript
163
+ @UseGuards(JwtGuard)
164
+ @Controller("protected")
165
+ export class ProtectedController {}
166
+ ```
167
+
168
+ ### OptionalJwtGuard
169
+
170
+ Allows anonymous access but extracts user if token is present:
171
+
172
+ ```typescript
173
+ @UseGuards(OptionalJwtGuard)
174
+ @Controller("optional")
175
+ export class OptionalController {
176
+ @Get()
177
+ getData(@CurrentUser() user?: any) {
178
+ if (user) {
179
+ return { message: "Authenticated", user };
180
+ }
181
+ return { message: "Anonymous" };
182
+ }
183
+ }
184
+ ```
185
+
186
+ ### PermissionsGuard
187
+
188
+ Requires specific permissions. Supports pipe-separated OR logic:
189
+
190
+ ```typescript
191
+ @UseGuards(JwtGuard, PermissionsGuard("product:create"))
192
+ @Controller("products")
193
+ export class ProductsController {
194
+ @Post()
195
+ createProduct() {
196
+ // Requires 'product:create' permission
197
+ }
198
+
199
+ @Patch(":id")
200
+ @UseGuards(PermissionsGuard("product:update|product:manage"))
201
+ updateProduct() {
202
+ // Requires 'product:update' OR 'product:manage'
203
+ }
204
+ }
205
+ ```
206
+
207
+ ### ApiKeyGuard
208
+
209
+ Validates `X-API-Key` header against `process.env.API_KEY`:
48
210
 
49
- ### Override JwtGuard In AppModule
211
+ ```typescript
212
+ @UseGuards(ApiKeyGuard)
213
+ @Controller("external")
214
+ export class ExternalController {}
215
+ ```
50
216
 
51
- If your app uses a different token payload shape or custom validation logic, override `JwtGuard` in `AppModule`.
52
- When `request.user` is already set by your guard, library `JwtGuard`/`OptionalJwtGuard` now skip token parsing and do not overwrite it.
217
+ ### VendorGuard
218
+
219
+ Checks vendor registration status (custom implementation required):
53
220
 
54
221
  ```typescript
55
- import { Injectable, ExecutionContext } from '@nestjs/common';
56
- import { JwtGuard } from '@zola_do/authorization';
222
+ @UseGuards(JwtGuard, VendorGuard())
223
+ @Controller("vendor")
224
+ export class VendorController {}
225
+ ```
226
+
227
+ ## Decorators
228
+
229
+ ### @CurrentUser()
230
+
231
+ Extract user from request:
232
+
233
+ ```typescript
234
+ @Get('profile')
235
+ getProfile(@CurrentUser() user: UserPayload) {
236
+ return user;
237
+ }
238
+
239
+ // With property extraction
240
+ @Get('profile')
241
+ getProfile(@CurrentUser('email') email: string) {
242
+ return { email };
243
+ }
244
+ ```
245
+
246
+ ### @AllowAnonymous()
247
+
248
+ Skip authentication for specific routes:
249
+
250
+ ```typescript
251
+ @Get('public')
252
+ @AllowAnonymous()
253
+ getPublicData() {
254
+ return { message: 'Public data' };
255
+ }
256
+ ```
257
+
258
+ ## JWT Payload
259
+
260
+ Default payload structure:
261
+
262
+ ```typescript
263
+ interface JwtPayload {
264
+ sub: string; // User ID
265
+ email: string; // User email
266
+ organization?: {
267
+ // Organization context
268
+ organizationId: string;
269
+ organizationName: string;
270
+ };
271
+ permissions?: string[]; // User permissions
272
+ iat?: number; // Issued at
273
+ exp?: number; // Expiration
274
+ }
275
+ ```
276
+
277
+ ## AuthHelper
278
+
279
+ Utility service for token operations:
280
+
281
+ ```typescript
282
+ import { AuthHelper } from "@zola_do/authorization";
283
+
284
+ @Injectable()
285
+ export class AuthService {
286
+ constructor(private readonly authHelper: AuthHelper) {}
287
+
288
+ async login(user: User) {
289
+ // Generate tokens
290
+ const tokens = this.authHelper.generateTokens(user);
291
+
292
+ // Hash password
293
+ const hashedPassword = await this.authHelper.hashPassword(plainPassword);
294
+
295
+ // Verify password
296
+ const isValid = await this.authHelper.verifyPassword(
297
+ plainPassword,
298
+ hashedPassword,
299
+ );
300
+
301
+ // Generate OTP
302
+ const otp = this.authHelper.generateOtp();
303
+
304
+ return { tokens, hashedPassword, isValid, otp };
305
+ }
306
+
307
+ async refreshTokens(refreshToken: string) {
308
+ return this.authHelper.refreshTokens(refreshToken);
309
+ }
310
+ }
311
+ ```
312
+
313
+ ### AuthHelper Methods
314
+
315
+ | Method | Description | Returns |
316
+ | -------------------------------- | ------------------------------ | ------------------------------- |
317
+ | `generateTokens(user)` | Create access + refresh tokens | `{ accessToken, refreshToken }` |
318
+ | `refreshTokens(refreshToken)` | Refresh using refresh token | `{ accessToken, refreshToken }` |
319
+ | `hashPassword(password)` | bcrypt hash | `string` |
320
+ | `verifyPassword(password, hash)` | Compare password to hash | `boolean` |
321
+ | `generateOtp(length)` | Generate numeric OTP | `string` |
322
+ | `decodeToken(token)` | Decode without verification | `JwtPayload` |
323
+
324
+ ## Token Generation Example
325
+
326
+ ```typescript
327
+ import { AuthHelper } from "@zola_do/authorization";
328
+ import { JwtPayload } from "@zola_do/authorization/helper";
329
+
330
+ @Controller("auth")
331
+ export class AuthController {
332
+ constructor(private readonly authHelper: AuthHelper) {}
333
+
334
+ @Post("login")
335
+ async login(@Body() dto: LoginDto) {
336
+ const user = await this.userService.validateUser(dto);
337
+
338
+ const payload: JwtPayload = {
339
+ sub: user.id,
340
+ email: user.email,
341
+ organization: {
342
+ organizationId: user.organizationId,
343
+ organizationName: user.organization.name,
344
+ },
345
+ permissions: user.permissions,
346
+ };
347
+
348
+ return this.authHelper.generateTokens(payload);
349
+ }
350
+
351
+ @Post("refresh")
352
+ async refresh(@Body("refreshToken") refreshToken: string) {
353
+ return this.authHelper.refreshTokens(refreshToken);
354
+ }
355
+ }
356
+ ```
357
+
358
+ ## Custom JwtGuard Override
359
+
360
+ Override the default JWT validation logic:
361
+
362
+ ```typescript
363
+ import { Injectable, ExecutionContext } from "@nestjs/common";
364
+ import { JwtGuard } from "@zola_do/authorization";
57
365
 
58
366
  @Injectable()
59
367
  export class AppJwtGuard extends JwtGuard {
60
368
  async canActivate(context: ExecutionContext): Promise<boolean> {
61
- // Add app-specific token parsing/validation logic here.
369
+ // Run default validation
370
+ const canActivate = await super.canActivate(context);
371
+ if (!canActivate) return false;
372
+
373
+ // Add custom logic
374
+ const request = context.switchToHttp().getRequest();
375
+ const user = request.user;
376
+
377
+ // Check additional conditions
378
+ if (user.status === "suspended") {
379
+ throw new ForbiddenException("Account suspended");
380
+ }
381
+
62
382
  return true;
63
383
  }
64
384
  }
65
385
  ```
66
386
 
387
+ Register in module:
388
+
67
389
  ```typescript
68
- import { Module } from '@nestjs/common';
69
- import { APP_GUARD } from '@nestjs/core';
70
- import { AuthorizationModule, JwtGuard } from '@zola_do/authorization';
71
- import { AppJwtGuard } from './guards/app-jwt.guard';
390
+ import { APP_GUARD } from "@nestjs/core";
391
+ import { AuthorizationModule, JwtGuard } from "@zola_do/authorization";
72
392
 
73
393
  @Module({
74
394
  imports: [AuthorizationModule],
75
395
  providers: [
76
396
  AppJwtGuard,
77
- // Used by controllers that reference @UseGuards(JwtGuard)
78
397
  { provide: JwtGuard, useExisting: AppJwtGuard },
79
- // Used as global guard
80
398
  { provide: APP_GUARD, useExisting: AppJwtGuard },
81
399
  ],
82
400
  })
83
401
  export class AppModule {}
84
402
  ```
85
403
 
86
- ### Protected Routes
404
+ ## Strategies
405
+
406
+ ### JwtStrategy
407
+
408
+ Passport strategy for access token validation:
87
409
 
88
410
  ```typescript
89
- import { Controller, Get } from '@nestjs/common';
90
- import { CurrentUser, JwtGuard, UseGuards } from '@zola_do/authorization';
411
+ import { JwtStrategy } from "@zola_do/authorization";
91
412
 
92
- @Controller('profile')
93
- @UseGuards(JwtGuard)
94
- export class ProfileController {
95
- @Get()
96
- getProfile(@CurrentUser() user: any) {
97
- return user; // Contains JWT payload (id, email, organization, etc.)
98
- }
99
- }
413
+ // Already registered by AuthorizationModule
414
+ // Customization via override (see above)
100
415
  ```
101
416
 
102
- ### Anonymous Routes
417
+ ### JwtRefreshTokenStrategy
418
+
419
+ Passport strategy for refresh token validation:
103
420
 
104
421
  ```typescript
105
- import { AllowAnonymous } from '@zola_do/authorization';
422
+ import { JwtRefreshTokenStrategy } from "@zola_do/authorization";
106
423
 
107
- @Get('public')
108
- @AllowAnonymous()
109
- getPublicData() {
110
- return { message: 'No auth required' };
111
- }
424
+ // Used for token refresh endpoints
425
+ // Validates against JWT_REFRESH_TOKEN_SECRET
112
426
  ```
113
427
 
114
- ### Permission Guards
428
+ ## Environment Variables
115
429
 
116
- Require specific permissions on routes:
430
+ | Variable | Description | Default |
431
+ | --------------------------- | ---------------------------- | -------- |
432
+ | `JWT_ACCESS_TOKEN_SECRET` | Access token signing secret | Required |
433
+ | `JWT_ACCESS_TOKEN_EXPIRES` | Access token TTL | `15m` |
434
+ | `JWT_REFRESH_TOKEN_SECRET` | Refresh token signing secret | Required |
435
+ | `JWT_REFRESH_TOKEN_EXPIRES` | Refresh token TTL | `7d` |
436
+ | `API_KEY` | API key for ApiKeyGuard | Optional |
117
437
 
118
- ```typescript
119
- import { PermissionsGuard } from '@zola_do/authorization';
438
+ ### Token Expiration Format
120
439
 
121
- @Post()
122
- @UseGuards(JwtGuard, PermissionsGuard('product:create'))
123
- createProduct(@Body() dto: CreateProductDto) {}
440
+ ```
441
+ 15m - 15 minutes
442
+ 1h - 1 hour
443
+ 7d - 7 days
444
+ 30d - 30 days
124
445
  ```
125
446
 
126
- ### API Key Guard
447
+ ## Permission System
127
448
 
128
- Protect routes with API key validation:
449
+ Permissions use pipe-separated OR logic:
129
450
 
130
451
  ```typescript
131
- import { ApiKeyGuard } from '@zola_do/authorization';
452
+ // Require any of these permissions
453
+ @UseGuards(PermissionsGuard('admin:write|manager:write|product:create'))
454
+ ```
132
455
 
133
- @UseGuards(ApiKeyGuard)
134
- @Get('api-data')
135
- getApiData() {}
456
+ ### Common Permission Patterns
457
+
458
+ | Pattern | Description |
459
+ | ----------------- | ----------------------------------------- |
460
+ | `resource:action` | Basic CRUD (product:create, product:read) |
461
+ | `resource:*` | All actions on resource |
462
+ | `*:read` | Read all resources |
463
+ | `admin:*` | Admin access |
464
+
465
+ ## Recommended Imports
466
+
467
+ Subpath imports are recommended for tree-shaking:
468
+
469
+ ```typescript
470
+ import { AuthorizationModule } from "@zola_do/authorization/module";
471
+ import {
472
+ JwtGuard,
473
+ PermissionsGuard,
474
+ ApiKeyGuard,
475
+ } from "@zola_do/authorization/guards";
476
+ import { AllowAnonymous, CurrentUser } from "@zola_do/authorization/decorators";
477
+ import { AuthHelper } from "@zola_do/authorization/helper";
478
+ import {
479
+ JwtStrategy,
480
+ JwtRefreshTokenStrategy,
481
+ } from "@zola_do/authorization/strategy";
136
482
  ```
137
483
 
138
- ### AuthHelper
484
+ Root import is supported for backward compatibility:
485
+
486
+ ```typescript
487
+ import {
488
+ AuthorizationModule,
489
+ JwtGuard,
490
+ AllowAnonymous,
491
+ AuthHelper,
492
+ } from "@zola_do/authorization";
493
+ ```
494
+
495
+ ## Optional Features
139
496
 
140
- Generate and verify tokens:
497
+ ### ThrottlerBehindProxyGuard
498
+
499
+ Rate limiting guard for applications behind a reverse proxy:
141
500
 
142
501
  ```typescript
143
- import { AuthHelper } from '@zola_do/authorization';
502
+ import { ThrottlerBehindProxyGuard } from "@zola_do/authorization/optional/throttler";
144
503
 
145
- @Injectable()
146
- export class AuthService {
147
- constructor(private readonly authHelper: AuthHelper) {}
504
+ @UseGuards(ThrottlerBehindProxyGuard)
505
+ @Controller("api")
506
+ export class ApiController {}
507
+ ```
148
508
 
149
- async login(user: User) {
150
- return this.authHelper.generateTokens(user);
151
- }
509
+ Requires `@nestjs/throttler` peer dependency.
510
+
511
+ ## API Reference
512
+
513
+ ### Guards
514
+
515
+ | Guard | Description | Auth Required |
516
+ | ------------------------------- | ----------------------- | -------------------- |
517
+ | `JwtGuard` | Bearer token validation | Yes (global default) |
518
+ | `OptionalJwtGuard` | Bearer token if present | No |
519
+ | `PermissionsGuard(permissions)` | Permission check | Yes |
520
+ | `ApiKeyGuard` | API key validation | Yes |
521
+ | `VendorGuard()` | Vendor status check | Yes |
522
+ | `ThrottlerBehindProxyGuard` | Rate limiting | Configurable |
523
+
524
+ ### Decorators
525
+
526
+ | Decorator | Description |
527
+ | ------------------------ | ----------------------------- |
528
+ | `@CurrentUser()` | Get current user from request |
529
+ | `@CurrentUser(property)` | Get specific property |
530
+ | `@AllowAnonymous()` | Skip authentication |
531
+
532
+ ### Strategies
533
+
534
+ | Strategy | Token Type | Secret |
535
+ | ------------------------- | ---------- | -------------------------- |
536
+ | `JwtStrategy` | Access | `JWT_ACCESS_TOKEN_SECRET` |
537
+ | `JwtRefreshTokenStrategy` | Refresh | `JWT_REFRESH_TOKEN_SECRET` |
538
+
539
+ ### AuthHelper
540
+
541
+ ```typescript
542
+ class AuthHelper {
543
+ generateTokens(payload: JwtPayload): {
544
+ accessToken: string;
545
+ refreshToken: string;
546
+ };
547
+ refreshTokens(
548
+ refreshToken: string,
549
+ ): Promise<{ accessToken: string; refreshToken: string }>;
550
+ hashPassword(password: string): Promise<string>;
551
+ verifyPassword(password: string, hashedPassword: string): Promise<boolean>;
552
+ generateOtp(length?: number): string;
553
+ decodeToken(token: string): JwtPayload;
152
554
  }
153
555
  ```
154
556
 
155
- ## Environment Variables
156
-
157
- | Variable | Description |
158
- |----------|-------------|
159
- | `JWT_ACCESS_TOKEN_SECRET` | Secret for access token signing |
160
- | `JWT_REFRESH_TOKEN_SECRET` | Secret for refresh token signing |
161
- | `JWT_ACCESS_TOKEN_EXPIRES` | Access token TTL (e.g. `15m`) |
162
- | `JWT_REFRESH_TOKEN_EXPIRES` | Refresh token TTL (e.g. `7d`) |
163
- | `API_KEY` | API key for `ApiKeyGuard` validation |
557
+ ## Troubleshooting
164
558
 
165
- ## Exports
559
+ ### Q: Token expired errors?
166
560
 
167
- - **Guards:** `JwtGuard`, `JwtRefreshGuard`, `PermissionsGuard`, `ApiKeyGuard`, `VendorGuard`, `OptionalJwtGuard`
168
- - **Decorators:** `@CurrentUser()`, `@AllowAnonymous()`
169
- - **Strategies:** `JwtStrategy`, `JwtRefreshTokenStrategy`
170
- - **Helpers:** `AuthHelper`
171
- - **Subpath entrypoints:** `@zola_do/authorization/module`, `@zola_do/authorization/decorators`, `@zola_do/authorization/guards`, `@zola_do/authorization/strategy`, `@zola_do/authorization/helper`, `@zola_do/authorization/models`
172
- - **Root entrypoint:** `@zola_do/authorization` (supported for backward compatibility)
561
+ Check `JWT_ACCESS_TOKEN_EXPIRES` environment variable and ensure clocks are synchronized.
173
562
 
174
- ### Optional Features
563
+ ### Q: User is undefined in @CurrentUser()?
175
564
 
176
- `ThrottlerBehindProxyGuard` is available as an optional export:
565
+ Ensure `JwtGuard` is active and token is valid:
177
566
 
178
567
  ```typescript
179
- import { ThrottlerBehindProxyGuard } from '@zola_do/authorization/optional/throttler';
568
+ @UseGuards(JwtGuard) // Add explicit guard if global is not configured
180
569
  ```
181
570
 
182
- When using this guard, install `@nestjs/throttler` in your app.
571
+ ### Q: Custom token payload?
572
+
573
+ Override `JwtStrategy` and `JwtGuard` to implement custom validation.
574
+
575
+ ### Q: Skip guard for specific routes?
576
+
577
+ ```typescript
578
+ @AllowAnonymous() // Works even with global JwtGuard
579
+ ```
183
580
 
184
581
  ## Related Packages
185
582
 
186
583
  - [@zola_do/crud](../crud) — Uses JwtGuard and PermissionsGuard
187
584
 
585
+ ## License
586
+
587
+ ISC
588
+
188
589
  ## Community
189
590
 
190
591
  - [Contributing](../../CONTRIBUTING.md)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zola_do/authorization",
3
- "version": "0.2.5",
3
+ "version": "0.2.6",
4
4
  "description": "JWT auth, guards, strategies for NestJS",
5
5
  "author": "zolaDO",
6
6
  "license": "ISC",