@vritti/api-sdk 0.0.2 → 0.0.3
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/README.md +504 -43
- package/dist/index.cjs +754 -336
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +394 -172
- package/dist/index.d.ts +394 -172
- package/dist/index.js +738 -331
- package/dist/index.js.map +1 -1
- package/package.json +7 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,83 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import * as _nestjs_common from '@nestjs/common';
|
|
2
|
+
import { DynamicModule, OnModuleDestroy, OnModuleInit, CanActivate, ExecutionContext } from '@nestjs/common';
|
|
3
|
+
import { ConfigService } from '@nestjs/config';
|
|
4
|
+
import { Reflector } from '@nestjs/core';
|
|
5
|
+
import { JwtService } from '@nestjs/jwt';
|
|
6
|
+
import { FastifyRequest } from 'fastify';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Global authentication configuration module
|
|
10
|
+
*
|
|
11
|
+
* This module provides:
|
|
12
|
+
* - JWT token verification (JwtModule)
|
|
13
|
+
* - Global authentication guard (VrittiAuthGuard)
|
|
14
|
+
* - Support for @Public and @Onboarding decorators
|
|
15
|
+
*
|
|
16
|
+
* ## Features:
|
|
17
|
+
* - Automatically applies VrittiAuthGuard to all routes
|
|
18
|
+
* - Configures JwtModule with JWT_SECRET from environment
|
|
19
|
+
* - Exports JwtModule for token generation in services
|
|
20
|
+
*
|
|
21
|
+
* ## Usage in Application:
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* // In app.module.ts
|
|
25
|
+
* @Module({
|
|
26
|
+
* imports: [
|
|
27
|
+
* ConfigModule.forRoot({ isGlobal: true }),
|
|
28
|
+
*
|
|
29
|
+
* // Auth configuration (global guard + JWT)
|
|
30
|
+
* AuthConfigModule.forRootAsync(),
|
|
31
|
+
*
|
|
32
|
+
* // Database configuration (Gateway mode)
|
|
33
|
+
* DatabaseModule.forServer({
|
|
34
|
+
* useFactory: (config: ConfigService) => ({
|
|
35
|
+
* primaryDb: {
|
|
36
|
+
* host: config.get('PRIMARY_DB_HOST'),
|
|
37
|
+
* // ... other config
|
|
38
|
+
* },
|
|
39
|
+
* prismaClientConstructor: PrismaClient,
|
|
40
|
+
* }),
|
|
41
|
+
* inject: [ConfigService],
|
|
42
|
+
* }),
|
|
43
|
+
* ],
|
|
44
|
+
* })
|
|
45
|
+
* export class AppModule {}
|
|
46
|
+
*
|
|
47
|
+
* ## Environment Variables Required:
|
|
48
|
+
* - JWT_SECRET: Secret key to verify access tokens (required)
|
|
49
|
+
* - JWT_REFRESH_SECRET: Secret key for refresh tokens (optional, falls back to JWT_SECRET)
|
|
50
|
+
*
|
|
51
|
+
* ## Bypass Authentication:
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* // Skip authentication on specific endpoints
|
|
55
|
+
* @Public()
|
|
56
|
+
* @Post('auth/login')
|
|
57
|
+
* async login() { ... }
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* // Onboarding endpoints (only accept onboarding tokens)
|
|
61
|
+
* @Onboarding()
|
|
62
|
+
* @Post('onboarding/verify-email')
|
|
63
|
+
* async verifyEmail(@Request() req) {
|
|
64
|
+
* const userId = req.user.id; // Available from guard
|
|
65
|
+
* ...
|
|
66
|
+
* }
|
|
67
|
+
*/
|
|
68
|
+
declare class AuthConfigModule {
|
|
69
|
+
/**
|
|
70
|
+
* Register the auth module with async configuration
|
|
71
|
+
*
|
|
72
|
+
* This method:
|
|
73
|
+
* 1. Configures JwtModule with JWT_SECRET from ConfigService
|
|
74
|
+
* 2. Provides VrittiAuthGuard globally (applies to all routes)
|
|
75
|
+
* 3. Exports JwtModule for use in other modules (e.g., for signing tokens)
|
|
76
|
+
*
|
|
77
|
+
* @returns Dynamic module configuration
|
|
78
|
+
*/
|
|
79
|
+
static forRootAsync(): DynamicModule;
|
|
80
|
+
}
|
|
3
81
|
|
|
4
82
|
/**
|
|
5
83
|
* Primary database connection configuration
|
|
@@ -66,19 +144,22 @@ interface DatabaseModuleOptions {
|
|
|
66
144
|
/**
|
|
67
145
|
* Tenant configuration stored in cloud database
|
|
68
146
|
* This is the shape of data returned from the tenant registry
|
|
147
|
+
*
|
|
148
|
+
* Note: Database configuration is now stored in a separate TenantDatabaseConfig table
|
|
149
|
+
* but is flattened into this interface for convenience.
|
|
69
150
|
*/
|
|
70
151
|
interface TenantInfo {
|
|
71
152
|
/** Unique tenant identifier */
|
|
72
153
|
id: string;
|
|
73
154
|
/** Human-readable tenant slug */
|
|
74
155
|
subdomain: string;
|
|
75
|
-
/** Tenant type */
|
|
76
|
-
type: 'SHARED' | '
|
|
156
|
+
/** Tenant type - SHARED or DEDICATED */
|
|
157
|
+
type: 'SHARED' | 'DEDICATED';
|
|
77
158
|
/** Tenant status */
|
|
78
159
|
status: string;
|
|
79
|
-
/** For
|
|
160
|
+
/** For SHARED tenants: schema name within the shared database */
|
|
80
161
|
schemaName?: string;
|
|
81
|
-
/** For
|
|
162
|
+
/** For DEDICATED tenants: database configuration (from TenantDatabaseConfig table) */
|
|
82
163
|
databaseName?: string;
|
|
83
164
|
databaseHost?: string;
|
|
84
165
|
databasePort?: number;
|
|
@@ -98,20 +179,24 @@ interface TenantInfo {
|
|
|
98
179
|
* - Support for both gateway and microservice modes
|
|
99
180
|
*
|
|
100
181
|
* ## Gateway Mode (API Gateway)
|
|
182
|
+
* - Use DatabaseModule.forServer() method
|
|
101
183
|
* - Automatically extracts tenant from subdomain, falls back to x-tenant-id header
|
|
102
184
|
* - Provide primaryDb configuration and prismaClientConstructor
|
|
103
185
|
* - Automatically queries primary DB for tenant config
|
|
104
|
-
* -
|
|
186
|
+
* - Automatically registers TenantContextInterceptor globally
|
|
187
|
+
* - No manual interceptor registration needed
|
|
105
188
|
*
|
|
106
|
-
* ## Microservice Mode
|
|
189
|
+
* ## Microservice Mode (RabbitMQ Workers)
|
|
190
|
+
* - Use DatabaseModule.forMicroservice() method
|
|
107
191
|
* - Only provide prismaClientConstructor
|
|
108
192
|
* - Tenant context comes from RabbitMQ messages
|
|
109
|
-
* -
|
|
193
|
+
* - Automatically registers MessageTenantContextInterceptor globally
|
|
194
|
+
* - No manual interceptor registration needed
|
|
110
195
|
*
|
|
111
196
|
* @example
|
|
112
197
|
* // Gateway configuration
|
|
113
|
-
* DatabaseModule.
|
|
114
|
-
*
|
|
198
|
+
* DatabaseModule.forServer({
|
|
199
|
+
* inject: [ConfigService],
|
|
115
200
|
* useFactory: (config: ConfigService) => ({
|
|
116
201
|
* primaryDb: {
|
|
117
202
|
* host: config.get('PRIMARY_DB_HOST'),
|
|
@@ -122,127 +207,80 @@ interface TenantInfo {
|
|
|
122
207
|
* },
|
|
123
208
|
* prismaClientConstructor: PrismaClient,
|
|
124
209
|
* }),
|
|
125
|
-
* inject: [ConfigService],
|
|
126
210
|
* })
|
|
127
211
|
*
|
|
128
212
|
* @example
|
|
129
213
|
* // Microservice configuration
|
|
130
|
-
* DatabaseModule.
|
|
131
|
-
*
|
|
214
|
+
* DatabaseModule.forMicroservice({
|
|
215
|
+
* inject: [ConfigService],
|
|
216
|
+
* useFactory: (config: ConfigService) => ({
|
|
217
|
+
* prismaClientConstructor: PrismaClient,
|
|
218
|
+
* }),
|
|
132
219
|
* })
|
|
133
220
|
*/
|
|
134
221
|
declare class DatabaseModule {
|
|
135
222
|
/**
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
* @param options Module configuration options
|
|
139
|
-
* @returns Dynamic module configuration
|
|
140
|
-
*/
|
|
141
|
-
static forRoot(options: DatabaseModuleOptions): DynamicModule;
|
|
142
|
-
/**
|
|
143
|
-
* Asynchronous configuration (recommended)
|
|
223
|
+
* Configure DatabaseModule for Gateway/HTTP mode (multi-tenant web servers)
|
|
144
224
|
*
|
|
145
|
-
*
|
|
225
|
+
* This mode is for API Gateways that handle HTTP requests:
|
|
226
|
+
* - Automatically registers TenantContextInterceptor
|
|
227
|
+
* - Extracts tenant from subdomain or x-tenant-id header
|
|
228
|
+
* - Queries primary database for tenant configuration
|
|
229
|
+
* - Provides PrimaryDatabaseService for tenant lookup
|
|
146
230
|
*
|
|
147
231
|
* @param options Async configuration options
|
|
148
|
-
* @returns Dynamic module configuration
|
|
232
|
+
* @returns Dynamic module configuration with HTTP interceptor
|
|
149
233
|
*
|
|
150
234
|
* @example
|
|
151
|
-
* DatabaseModule.
|
|
152
|
-
*
|
|
153
|
-
* useFactory:
|
|
154
|
-
*
|
|
235
|
+
* DatabaseModule.forServer({
|
|
236
|
+
* inject: [ConfigService],
|
|
237
|
+
* useFactory: (config: ConfigService) => ({
|
|
238
|
+
* primaryDb: {
|
|
239
|
+
* host: config.get('PRIMARY_DB_HOST'),
|
|
240
|
+
* port: config.get('PRIMARY_DB_PORT'),
|
|
241
|
+
* username: config.get('PRIMARY_DB_USERNAME'),
|
|
242
|
+
* password: config.get('PRIMARY_DB_PASSWORD'),
|
|
243
|
+
* database: config.get('PRIMARY_DB_DATABASE'),
|
|
244
|
+
* },
|
|
155
245
|
* prismaClientConstructor: PrismaClient,
|
|
156
|
-
* tenantResolver: 'subdomain',
|
|
157
246
|
* }),
|
|
158
|
-
* inject: [ConfigService],
|
|
159
247
|
* })
|
|
160
248
|
*/
|
|
161
|
-
static
|
|
249
|
+
static forServer(options: {
|
|
162
250
|
useFactory: (...args: any[]) => Promise<DatabaseModuleOptions> | DatabaseModuleOptions;
|
|
163
251
|
inject?: any[];
|
|
164
252
|
}): DynamicModule;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Service responsible for querying the primary database to resolve tenant configurations
|
|
169
|
-
*
|
|
170
|
-
* This service:
|
|
171
|
-
* - Connects to the primary database (tenant registry)
|
|
172
|
-
* - Queries tenant metadata (database location, credentials, etc.)
|
|
173
|
-
* - Caches tenant configs in memory to reduce database load
|
|
174
|
-
* - Only used in GATEWAY MODE (microservices receive tenant config from messages)
|
|
175
|
-
*
|
|
176
|
-
* @example
|
|
177
|
-
* // In API Gateway
|
|
178
|
-
* const config = await primaryDatabase.getTenantConfig('acme');
|
|
179
|
-
* // Returns: { id, slug, type, databaseHost, databaseName, ... }
|
|
180
|
-
*/
|
|
181
|
-
declare class PrimaryDatabaseService implements OnModuleInit, OnModuleDestroy {
|
|
182
|
-
private readonly options;
|
|
183
|
-
private readonly logger;
|
|
184
|
-
/** Primary database client for querying tenant registry */
|
|
185
|
-
private primaryDbClient;
|
|
186
|
-
/** In-memory cache: Map<tenantIdentifier, TenantConfig> */
|
|
187
|
-
private readonly tenantConfigCache;
|
|
188
|
-
/** Cache TTL in milliseconds */
|
|
189
|
-
private readonly cacheTTL;
|
|
190
|
-
constructor(options: DatabaseModuleOptions);
|
|
191
|
-
onModuleInit(): Promise<void>;
|
|
192
|
-
/**
|
|
193
|
-
* Initialize connection to primary database
|
|
194
|
-
*/
|
|
195
|
-
private initializePrimaryDbClient;
|
|
196
|
-
/**
|
|
197
|
-
* Build connection URL from primary database properties
|
|
198
|
-
*/
|
|
199
|
-
private buildPrimaryDbUrl;
|
|
200
|
-
/**
|
|
201
|
-
* Mask password in connection URL for logging
|
|
202
|
-
*/
|
|
203
|
-
private maskPassword;
|
|
204
|
-
/**
|
|
205
|
-
* Get tenant configuration by identifier (ID or slug)
|
|
206
|
-
*
|
|
207
|
-
* @param tenantIdentifier Tenant ID or slug
|
|
208
|
-
* @returns Tenant configuration or null if not found
|
|
209
|
-
*/
|
|
210
|
-
getTenantInfo(tenantIdentifier: string): Promise<TenantInfo | null>;
|
|
211
|
-
/**
|
|
212
|
-
* Cache tenant information with TTL
|
|
213
|
-
*/
|
|
214
|
-
private cacheInfo;
|
|
215
253
|
/**
|
|
216
|
-
*
|
|
254
|
+
* Configure DatabaseModule for Microservice/Messaging mode (RabbitMQ workers)
|
|
217
255
|
*
|
|
218
|
-
*
|
|
256
|
+
* This mode is for microservices that process messages from queues:
|
|
257
|
+
* - Automatically registers MessageTenantContextInterceptor
|
|
258
|
+
* - Extracts tenant from RabbitMQ message patterns
|
|
259
|
+
* - No primary database needed (tenant comes from message context)
|
|
219
260
|
*
|
|
220
|
-
* @param
|
|
221
|
-
|
|
222
|
-
clearTenantCache(tenantIdentifier: string): void;
|
|
223
|
-
/**
|
|
224
|
-
* Clear all cached tenant configurations
|
|
225
|
-
*/
|
|
226
|
-
clearAllCaches(): void;
|
|
227
|
-
/**
|
|
228
|
-
* Get primary database client for direct database access
|
|
229
|
-
*
|
|
230
|
-
* This is useful for platform admin operations (creating tenants, billing, etc.)
|
|
261
|
+
* @param options Async configuration options
|
|
262
|
+
* @returns Dynamic module configuration with message interceptor
|
|
231
263
|
*
|
|
232
|
-
* @
|
|
233
|
-
*
|
|
264
|
+
* @example
|
|
265
|
+
* DatabaseModule.forMicroservice({
|
|
266
|
+
* inject: [ConfigService],
|
|
267
|
+
* useFactory: (config: ConfigService) => ({
|
|
268
|
+
* prismaClientConstructor: PrismaClient,
|
|
269
|
+
* }),
|
|
270
|
+
* })
|
|
234
271
|
*/
|
|
235
|
-
|
|
272
|
+
static forMicroservice(options: {
|
|
273
|
+
useFactory: (...args: any[]) => Promise<DatabaseModuleOptions> | DatabaseModuleOptions;
|
|
274
|
+
inject?: any[];
|
|
275
|
+
}): DynamicModule;
|
|
236
276
|
/**
|
|
237
|
-
*
|
|
277
|
+
* Internal helper to create dynamic module with conditional interceptor registration
|
|
238
278
|
*
|
|
239
|
-
*
|
|
240
|
-
*
|
|
241
|
-
* @
|
|
242
|
-
* @returns Decrypted value
|
|
279
|
+
* @param options Configuration options
|
|
280
|
+
* @param mode Mode of operation (gateway or microservice)
|
|
281
|
+
* @returns Dynamic module configuration
|
|
243
282
|
*/
|
|
244
|
-
private
|
|
245
|
-
onModuleDestroy(): Promise<void>;
|
|
283
|
+
private static createDynamicModule;
|
|
246
284
|
}
|
|
247
285
|
|
|
248
286
|
/**
|
|
@@ -390,83 +428,209 @@ declare class TenantDatabaseService implements OnModuleDestroy {
|
|
|
390
428
|
}
|
|
391
429
|
|
|
392
430
|
/**
|
|
393
|
-
*
|
|
394
|
-
*
|
|
395
|
-
* This
|
|
396
|
-
*
|
|
397
|
-
*
|
|
398
|
-
*
|
|
399
|
-
*
|
|
400
|
-
* Expected message format:
|
|
401
|
-
* {
|
|
402
|
-
* dto: { ... },
|
|
403
|
-
* tenant: {
|
|
404
|
-
* tenantId: 'abc-123',
|
|
405
|
-
* tenantSlug: 'acme',
|
|
406
|
-
* tenantType: 'ENTERPRISE',
|
|
407
|
-
* databaseHost: 'enterprise-1.aws.com',
|
|
408
|
-
* databaseName: 'acme_db',
|
|
409
|
-
* ...
|
|
410
|
-
* }
|
|
411
|
-
* }
|
|
431
|
+
* Service responsible for querying the primary database to resolve tenant configurations
|
|
432
|
+
*
|
|
433
|
+
* This service:
|
|
434
|
+
* - Connects to the primary database (tenant registry)
|
|
435
|
+
* - Queries tenant metadata (database location, credentials, etc.)
|
|
436
|
+
* - Caches tenant configs in memory to reduce database load
|
|
437
|
+
* - Only used in GATEWAY MODE (microservices receive tenant config from messages)
|
|
412
438
|
*
|
|
413
439
|
* @example
|
|
414
|
-
* // In
|
|
415
|
-
*
|
|
416
|
-
*
|
|
417
|
-
* useClass: MessageTenantContextInterceptor,
|
|
418
|
-
* }
|
|
440
|
+
* // In API Gateway
|
|
441
|
+
* const config = await primaryDatabase.getTenantConfig('acme');
|
|
442
|
+
* // Returns: { id, slug, type, databaseHost, databaseName, ... }
|
|
419
443
|
*/
|
|
420
|
-
declare class
|
|
421
|
-
private readonly
|
|
444
|
+
declare class PrimaryDatabaseService implements OnModuleInit, OnModuleDestroy {
|
|
445
|
+
private readonly options;
|
|
422
446
|
private readonly logger;
|
|
423
|
-
|
|
424
|
-
|
|
447
|
+
/** Primary database client for querying tenant registry */
|
|
448
|
+
private primaryDbClient;
|
|
449
|
+
/** In-memory cache: Map<tenantIdentifier, TenantConfig> */
|
|
450
|
+
private readonly tenantConfigCache;
|
|
451
|
+
/** Cache TTL in milliseconds */
|
|
452
|
+
private readonly cacheTTL;
|
|
453
|
+
constructor(options: DatabaseModuleOptions);
|
|
454
|
+
onModuleInit(): Promise<void>;
|
|
455
|
+
/**
|
|
456
|
+
* Initialize connection to primary database
|
|
457
|
+
*/
|
|
458
|
+
private initializePrimaryDbClient;
|
|
459
|
+
/**
|
|
460
|
+
* Build connection URL from primary database properties
|
|
461
|
+
*/
|
|
462
|
+
private buildPrimaryDbUrl;
|
|
463
|
+
/**
|
|
464
|
+
* Mask password in connection URL for logging
|
|
465
|
+
*/
|
|
466
|
+
private maskPassword;
|
|
467
|
+
/**
|
|
468
|
+
* Get tenant configuration by identifier (ID or slug)
|
|
469
|
+
*
|
|
470
|
+
* @param tenantIdentifier Tenant ID or slug
|
|
471
|
+
* @returns Tenant configuration or null if not found
|
|
472
|
+
*/
|
|
473
|
+
getTenantInfo(tenantIdentifier: string): Promise<TenantInfo | null>;
|
|
474
|
+
/**
|
|
475
|
+
* Cache tenant information with TTL
|
|
476
|
+
*/
|
|
477
|
+
private cacheInfo;
|
|
478
|
+
/**
|
|
479
|
+
* Clear cached tenant information
|
|
480
|
+
*
|
|
481
|
+
* Useful when tenant settings are updated and cache needs to be invalidated
|
|
482
|
+
*
|
|
483
|
+
* @param tenantIdentifier Tenant ID or slug
|
|
484
|
+
*/
|
|
485
|
+
clearTenantCache(tenantIdentifier: string): void;
|
|
486
|
+
/**
|
|
487
|
+
* Clear all cached tenant configurations
|
|
488
|
+
*/
|
|
489
|
+
clearAllCaches(): void;
|
|
490
|
+
/**
|
|
491
|
+
* Get primary database client for direct database access
|
|
492
|
+
*
|
|
493
|
+
* This is useful for platform admin operations (creating tenants, billing, etc.)
|
|
494
|
+
*
|
|
495
|
+
* @returns Primary database client instance
|
|
496
|
+
* @throws Error if primary database client is not initialized
|
|
497
|
+
*/
|
|
498
|
+
getPrimaryDbClient<T = any>(): T;
|
|
499
|
+
/**
|
|
500
|
+
* Decrypt database credentials
|
|
501
|
+
*
|
|
502
|
+
* Override this method to implement your encryption strategy
|
|
503
|
+
*
|
|
504
|
+
* @param encrypted Encrypted value
|
|
505
|
+
* @returns Decrypted value
|
|
506
|
+
*/
|
|
507
|
+
private decrypt;
|
|
508
|
+
onModuleDestroy(): Promise<void>;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
declare class RequestService {
|
|
512
|
+
private readonly request;
|
|
513
|
+
constructor(request: FastifyRequest);
|
|
514
|
+
/**
|
|
515
|
+
* Extract tenant identifier from request headers
|
|
516
|
+
* Priority: x-tenant-id > x-subdomain
|
|
517
|
+
* @returns Tenant identifier or null if not found
|
|
518
|
+
*/
|
|
519
|
+
getTenantIdentifier(): string | null;
|
|
520
|
+
/**
|
|
521
|
+
* Extract access token from Authorization header
|
|
522
|
+
* Format: "Bearer <token>"
|
|
523
|
+
* @returns Access token or null if not found
|
|
524
|
+
*/
|
|
525
|
+
getAccessToken(): string | null;
|
|
526
|
+
/**
|
|
527
|
+
* Extract refresh token from session-id cookie
|
|
528
|
+
* Cookie name: session-id
|
|
529
|
+
* @returns Refresh token or null if not found
|
|
530
|
+
*/
|
|
531
|
+
getRefreshToken(): string | null;
|
|
532
|
+
/**
|
|
533
|
+
* Get a specific header value
|
|
534
|
+
* @param key Header key
|
|
535
|
+
* @returns Header value (string, array, or undefined)
|
|
536
|
+
*/
|
|
537
|
+
getHeader(key: string): string | string[] | undefined;
|
|
425
538
|
/**
|
|
426
|
-
*
|
|
539
|
+
* Get all headers
|
|
540
|
+
* @returns Record of all headers
|
|
427
541
|
*/
|
|
428
|
-
|
|
542
|
+
getAllHeaders(): FastifyRequest['headers'];
|
|
429
543
|
}
|
|
430
544
|
|
|
431
545
|
/**
|
|
432
|
-
*
|
|
546
|
+
* Vritti Authentication Guard - Validates JWT tokens and tenant context
|
|
547
|
+
*
|
|
548
|
+
* This guard performs comprehensive validation and attaches user data to request.
|
|
549
|
+
*
|
|
550
|
+
* Validation Flow:
|
|
551
|
+
* 1. Checks if endpoint is marked with @Public() decorator → skip all validation
|
|
552
|
+
* 2. Checks if endpoint is marked with @Onboarding() decorator:
|
|
553
|
+
* - Requires token type='onboarding'
|
|
554
|
+
* - Validates JWT signature and expiry only
|
|
555
|
+
* - Skips tenant and refresh token validation
|
|
556
|
+
* - Attaches user data to request.user
|
|
557
|
+
* 3. For regular endpoints (no decorator):
|
|
558
|
+
* - Rejects tokens with type='onboarding'
|
|
559
|
+
* - Validates access token (JWT signature, expiry, nbf)
|
|
560
|
+
* - Validates refresh token from session-id cookie
|
|
561
|
+
* - Validates tenant exists and is ACTIVE
|
|
562
|
+
* - Attaches user data to request.user
|
|
563
|
+
*
|
|
564
|
+
* Token Format:
|
|
565
|
+
* - Access Token: "Authorization: Bearer <jwt_token>"
|
|
566
|
+
* - Refresh Token: "session-id" cookie
|
|
433
567
|
*
|
|
434
|
-
*
|
|
435
|
-
*
|
|
436
|
-
*
|
|
437
|
-
* 3. Stores tenant info in REQUEST-SCOPED TenantContextService
|
|
568
|
+
* Token Types:
|
|
569
|
+
* - type='onboarding': Limited access during registration flow (@Onboarding endpoints only)
|
|
570
|
+
* - type='access': Full access to authenticated endpoints
|
|
438
571
|
*
|
|
439
|
-
*
|
|
440
|
-
* -
|
|
441
|
-
* -
|
|
572
|
+
* Environment Variables Required:
|
|
573
|
+
* - JWT_SECRET: Secret key to verify access tokens (required)
|
|
574
|
+
* - JWT_REFRESH_SECRET: Secret key for refresh tokens (optional, falls back to JWT_SECRET)
|
|
442
575
|
*
|
|
443
|
-
*
|
|
576
|
+
* Error Responses:
|
|
577
|
+
* - 401: Invalid/expired access token
|
|
578
|
+
* - 401: Invalid/expired refresh token
|
|
579
|
+
* - 401: Tenant not found or inactive
|
|
580
|
+
* - 401: Tenant identifier not found
|
|
581
|
+
* - 401: Token type mismatch (onboarding token on regular endpoint or vice versa)
|
|
582
|
+
*
|
|
583
|
+
* @example
|
|
584
|
+
* // Automatically registered by AuthConfigModule.forRootAsync()
|
|
585
|
+
* // No manual registration needed
|
|
586
|
+
* //
|
|
587
|
+
* // Internal registration uses useExisting pattern:
|
|
588
|
+
* // providers: [
|
|
589
|
+
* // VrittiAuthGuard,
|
|
590
|
+
* // {
|
|
591
|
+
* // provide: APP_GUARD,
|
|
592
|
+
* // useExisting: VrittiAuthGuard,
|
|
593
|
+
* // },
|
|
594
|
+
* // ]
|
|
595
|
+
*
|
|
596
|
+
* @example
|
|
597
|
+
* // Bypass guard with @Public() decorator
|
|
598
|
+
* @Public()
|
|
599
|
+
* @Post('auth/login')
|
|
600
|
+
* async login(@Body() dto: LoginDto) { ... }
|
|
444
601
|
*
|
|
445
602
|
* @example
|
|
446
|
-
* //
|
|
447
|
-
*
|
|
603
|
+
* // Restrict to onboarding tokens with @Onboarding() decorator
|
|
604
|
+
* @Onboarding()
|
|
605
|
+
* @Post('onboarding/verify-email')
|
|
606
|
+
* async verifyEmail(@Request() req) {
|
|
607
|
+
* const userId = req.user.id; // Available from guard
|
|
608
|
+
* ...
|
|
609
|
+
* }
|
|
448
610
|
*/
|
|
449
|
-
declare class
|
|
450
|
-
private readonly
|
|
611
|
+
declare class VrittiAuthGuard implements CanActivate {
|
|
612
|
+
private readonly reflector;
|
|
613
|
+
private readonly configService;
|
|
614
|
+
private readonly jwtService;
|
|
451
615
|
private readonly primaryDatabase;
|
|
452
|
-
private readonly
|
|
616
|
+
private readonly requestService;
|
|
453
617
|
private readonly logger;
|
|
454
|
-
constructor(
|
|
455
|
-
|
|
618
|
+
constructor(reflector: Reflector, configService: ConfigService, jwtService: JwtService, primaryDatabase: PrimaryDatabaseService, requestService: RequestService);
|
|
619
|
+
canActivate(context: ExecutionContext): Promise<boolean>;
|
|
456
620
|
/**
|
|
457
|
-
*
|
|
621
|
+
* Validate access token with proper expiry checks
|
|
622
|
+
* Throws UnauthorizedException if token is invalid or expired
|
|
458
623
|
*/
|
|
459
|
-
private
|
|
624
|
+
private validateAccessToken;
|
|
460
625
|
/**
|
|
461
|
-
*
|
|
462
|
-
*
|
|
626
|
+
* Validate refresh token with proper expiry checks
|
|
627
|
+
* Throws UnauthorizedException if token is invalid or expired
|
|
463
628
|
*/
|
|
464
|
-
private
|
|
629
|
+
private validateRefreshToken;
|
|
465
630
|
/**
|
|
466
|
-
*
|
|
467
|
-
* Checks x-tenant-id and x-subdomain headers
|
|
631
|
+
* Helper to validate refresh token with specific secret
|
|
468
632
|
*/
|
|
469
|
-
private
|
|
633
|
+
private validateRefreshTokenWithSecret;
|
|
470
634
|
}
|
|
471
635
|
|
|
472
636
|
/**
|
|
@@ -488,9 +652,9 @@ declare class TenantContextInterceptor implements NestInterceptor {
|
|
|
488
652
|
* @Get('info')
|
|
489
653
|
* async getTenantInfo(@Tenant() tenant: TenantInfo) {
|
|
490
654
|
* return {
|
|
491
|
-
*
|
|
492
|
-
*
|
|
493
|
-
*
|
|
655
|
+
* id: tenant.id,
|
|
656
|
+
* subdomain: tenant.subdomain,
|
|
657
|
+
* type: tenant.type,
|
|
494
658
|
* };
|
|
495
659
|
* }
|
|
496
660
|
*
|
|
@@ -501,7 +665,7 @@ declare class TenantContextInterceptor implements NestInterceptor {
|
|
|
501
665
|
* @Body() dto: CreateUserDto,
|
|
502
666
|
* @Tenant() tenant: TenantInfo,
|
|
503
667
|
* ) {
|
|
504
|
-
* this.logger.log(`Creating user for tenant: ${tenant.
|
|
668
|
+
* this.logger.log(`Creating user for tenant: ${tenant.subdomain}`);
|
|
505
669
|
* // ...
|
|
506
670
|
* }
|
|
507
671
|
*
|
|
@@ -509,7 +673,7 @@ declare class TenantContextInterceptor implements NestInterceptor {
|
|
|
509
673
|
* // Conditional business logic
|
|
510
674
|
* @Get('features')
|
|
511
675
|
* async getFeatures(@Tenant() tenant: TenantInfo) {
|
|
512
|
-
* if (tenant.
|
|
676
|
+
* if (tenant.type === 'ENTERPRISE') {
|
|
513
677
|
* return ['feature-a', 'feature-b', 'feature-c'];
|
|
514
678
|
* }
|
|
515
679
|
* return ['feature-a'];
|
|
@@ -518,17 +682,75 @@ declare class TenantContextInterceptor implements NestInterceptor {
|
|
|
518
682
|
declare const Tenant: (...dataOrPipes: unknown[]) => ParameterDecorator;
|
|
519
683
|
|
|
520
684
|
/**
|
|
521
|
-
*
|
|
685
|
+
* Onboarding Decorator - Marks endpoints that require onboarding token
|
|
686
|
+
*
|
|
687
|
+
* Use this decorator on controllers or route handlers that should only be
|
|
688
|
+
* accessible during the onboarding flow with JWT tokens containing type='onboarding'.
|
|
689
|
+
*
|
|
690
|
+
* These endpoints:
|
|
691
|
+
* - Accept ONLY tokens with type='onboarding'
|
|
692
|
+
* - Reject regular access tokens (type='access')
|
|
693
|
+
* - Skip tenant validation and refresh token checks
|
|
694
|
+
* - Only validate JWT signature and expiry
|
|
695
|
+
*
|
|
696
|
+
* Useful for:
|
|
697
|
+
* - Email/phone verification during onboarding
|
|
698
|
+
* - Onboarding status checks
|
|
699
|
+
* - Resending OTPs during registration
|
|
700
|
+
*
|
|
701
|
+
* @example
|
|
702
|
+
* // On a controller method
|
|
703
|
+
* @Post('verify-email')
|
|
704
|
+
* @Onboarding()
|
|
705
|
+
* async verifyEmail(@Request() req, @Body() dto: VerifyEmailDto) {
|
|
706
|
+
* const userId = req.user.id; // Available from VrittiAuthGuard
|
|
707
|
+
* return this.service.verifyEmail(userId, dto.otp);
|
|
708
|
+
* }
|
|
709
|
+
*
|
|
710
|
+
* @example
|
|
711
|
+
* // Multiple onboarding endpoints
|
|
712
|
+
* @Controller('onboarding')
|
|
713
|
+
* export class OnboardingController {
|
|
714
|
+
* @Post('verify-email')
|
|
715
|
+
* @Onboarding()
|
|
716
|
+
* async verifyEmail() { ... }
|
|
717
|
+
*
|
|
718
|
+
* @Post('resend-otp')
|
|
719
|
+
* @Onboarding()
|
|
720
|
+
* async resendOtp() { ... }
|
|
721
|
+
* }
|
|
722
|
+
*/
|
|
723
|
+
declare const Onboarding: () => _nestjs_common.CustomDecorator<string>;
|
|
724
|
+
|
|
725
|
+
/**
|
|
726
|
+
* Public Decorator - Marks endpoints that don't require authentication
|
|
522
727
|
*
|
|
523
|
-
*
|
|
524
|
-
*
|
|
728
|
+
* Use this decorator on controllers or route handlers to bypass VrittiAuthGuard
|
|
729
|
+
* tenant validation. Useful for:
|
|
730
|
+
* - Login/signup endpoints
|
|
731
|
+
* - Health checks
|
|
732
|
+
* - Public documentation endpoints
|
|
733
|
+
* - Webhook endpoints that don't require tenant context
|
|
525
734
|
*
|
|
526
735
|
* @example
|
|
527
|
-
*
|
|
528
|
-
*
|
|
529
|
-
*
|
|
530
|
-
*
|
|
736
|
+
* // On a controller method
|
|
737
|
+
* @Public()
|
|
738
|
+
* @Post('auth/login')
|
|
739
|
+
* async login(@Body() dto: LoginDto) {
|
|
740
|
+
* return this.authService.login(dto);
|
|
741
|
+
* }
|
|
742
|
+
*
|
|
743
|
+
* @example
|
|
744
|
+
* // On an entire controller
|
|
745
|
+
* @Public()
|
|
746
|
+
* @Controller('health')
|
|
747
|
+
* export class HealthController {
|
|
748
|
+
* @Get()
|
|
749
|
+
* check() {
|
|
750
|
+
* return { status: 'ok' };
|
|
751
|
+
* }
|
|
752
|
+
* }
|
|
531
753
|
*/
|
|
532
|
-
declare
|
|
754
|
+
declare const Public: () => _nestjs_common.CustomDecorator<string>;
|
|
533
755
|
|
|
534
|
-
export { DatabaseModule, type DatabaseModuleOptions,
|
|
756
|
+
export { AuthConfigModule, DatabaseModule, type DatabaseModuleOptions, Onboarding, PrimaryDatabaseService, type PrimaryDbConfig, Public, Tenant, TenantContextService, TenantDatabaseService, type TenantInfo, VrittiAuthGuard };
|