@flusys/nestjs-auth 1.1.0-beta → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +633 -468
- package/cjs/config/auth.constants.js +8 -0
- package/cjs/controllers/authentication.controller.js +175 -167
- package/cjs/controllers/branch.controller.js +44 -1
- package/cjs/controllers/company-selection.controller.js +27 -20
- package/cjs/controllers/company.controller.js +44 -1
- package/cjs/controllers/email-verification.controller.js +220 -0
- package/cjs/controllers/index.js +1 -0
- package/cjs/controllers/user-permission.controller.js +82 -154
- package/cjs/controllers/user.controller.js +68 -15
- package/cjs/docs/auth-swagger.config.js +14 -7
- package/cjs/dtos/authentication.dto.js +16 -43
- package/cjs/dtos/index.js +0 -1
- package/cjs/dtos/user-permission.dto.js +28 -0
- package/cjs/dtos/user.dto.js +19 -4
- package/cjs/entities/index.js +16 -17
- package/cjs/entities/user-company-permission.entity.js +3 -9
- package/cjs/interceptors/clear-token.interceptor.js +10 -23
- package/cjs/interceptors/set-token.interceptor.js +15 -55
- package/cjs/interfaces/auth-email-provider.interface.js +1 -1
- package/cjs/modules/auth.module.js +79 -154
- package/cjs/services/auth-config.service.js +33 -35
- package/cjs/services/auth-datasource.provider.js +18 -42
- package/cjs/services/auth-email.service.js +89 -85
- package/cjs/services/authentication.service.js +248 -414
- package/cjs/services/branch.service.js +9 -4
- package/cjs/services/company-selection-session.service.js +1 -1
- package/cjs/services/user-permission.service.js +43 -73
- package/cjs/services/user.service.js +78 -64
- package/cjs/strategies/jwt.strategy.js +44 -44
- package/cjs/validators/password.validator.js +45 -0
- package/config/auth.constants.d.ts +2 -0
- package/controllers/authentication.controller.d.ts +4 -10
- package/controllers/company-selection.controller.d.ts +6 -2
- package/controllers/email-verification.controller.d.ts +20 -0
- package/controllers/index.d.ts +1 -0
- package/controllers/user-permission.controller.d.ts +5 -4
- package/controllers/user.controller.d.ts +6 -0
- package/dtos/authentication.dto.d.ts +0 -4
- package/dtos/company-branch.dto.d.ts +0 -1
- package/dtos/company.dto.d.ts +0 -1
- package/dtos/index.d.ts +0 -1
- package/dtos/user-permission.dto.d.ts +4 -0
- package/dtos/user.dto.d.ts +1 -3
- package/entities/index.d.ts +5 -10
- package/entities/user-company-permission.entity.d.ts +1 -3
- package/fesm/config/auth.constants.js +6 -13
- package/fesm/controllers/authentication.controller.js +178 -170
- package/fesm/controllers/branch.controller.js +45 -2
- package/fesm/controllers/company-selection.controller.js +28 -21
- package/fesm/controllers/company.controller.js +45 -2
- package/fesm/controllers/email-verification.controller.js +210 -0
- package/fesm/controllers/index.js +1 -0
- package/fesm/controllers/user-permission.controller.js +84 -156
- package/fesm/controllers/user.controller.js +71 -18
- package/fesm/docs/auth-swagger.config.js +14 -7
- package/fesm/dtos/authentication.dto.js +17 -44
- package/fesm/dtos/index.js +0 -1
- package/fesm/dtos/user-permission.dto.js +28 -0
- package/fesm/dtos/user.dto.js +20 -5
- package/fesm/entities/index.js +17 -9
- package/fesm/entities/user-company-permission.entity.js +3 -9
- package/fesm/interceptors/clear-token.interceptor.js +9 -22
- package/fesm/interceptors/set-token.interceptor.js +14 -54
- package/fesm/interfaces/auth-email-provider.interface.js +1 -1
- package/fesm/modules/auth.module.js +82 -157
- package/fesm/services/auth-config.service.js +34 -36
- package/fesm/services/auth-datasource.provider.js +19 -43
- package/fesm/services/auth-email.service.js +90 -86
- package/fesm/services/authentication.service.js +248 -414
- package/fesm/services/branch.service.js +9 -4
- package/fesm/services/company-selection-session.service.js +1 -1
- package/fesm/services/user-permission.service.js +44 -74
- package/fesm/services/user.service.js +79 -65
- package/fesm/strategies/jwt.strategy.js +44 -44
- package/fesm/validators/password.validator.js +48 -0
- package/interceptors/clear-token.interceptor.d.ts +0 -1
- package/interceptors/set-token.interceptor.d.ts +1 -1
- package/interfaces/authentication.interface.d.ts +0 -4
- package/interfaces/user-enricher.interface.d.ts +1 -4
- package/modules/auth.module.d.ts +9 -4
- package/package.json +4 -4
- package/services/auth-config.service.d.ts +7 -8
- package/services/auth-datasource.provider.d.ts +3 -4
- package/services/auth-email.service.d.ts +8 -4
- package/services/authentication.service.d.ts +16 -16
- package/services/company-selection-session.service.d.ts +1 -1
- package/services/user-permission.service.d.ts +6 -9
- package/services/user.service.d.ts +3 -0
- package/strategies/jwt.strategy.d.ts +4 -1
- package/validators/password.validator.d.ts +10 -0
- package/cjs/dtos/user-company-permission.dto.js +0 -110
- package/dtos/user-company-permission.dto.d.ts +0 -10
- package/fesm/dtos/user-company-permission.dto.js +0 -98
package/README.md
CHANGED
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
# Authentication Package Guide
|
|
2
2
|
|
|
3
3
|
> **Package:** `@flusys/nestjs-auth`
|
|
4
|
-
> **
|
|
5
|
-
|
|
6
|
-
This comprehensive guide covers the authentication package - flexible auth system for NestJS applications.
|
|
4
|
+
> **Version:** 1.1.0
|
|
5
|
+
> **Type:** Authentication system with JWT, email verification, multi-tenant, and company/branch support
|
|
7
6
|
|
|
8
7
|
## Table of Contents
|
|
9
8
|
|
|
10
9
|
- [Installation](#installation)
|
|
11
10
|
- [Quick Start](#quick-start)
|
|
12
11
|
- [Configuration](#configuration)
|
|
12
|
+
- [Constants](#constants)
|
|
13
|
+
- [Entities](#entities)
|
|
14
|
+
- [Provider Interfaces](#provider-interfaces)
|
|
15
|
+
- [Services](#services)
|
|
13
16
|
- [API Endpoints](#api-endpoints)
|
|
14
|
-
- [Architecture](#architecture)
|
|
15
17
|
- [Login Flows](#login-flows)
|
|
18
|
+
- [Email Verification Flow](#email-verification-flow)
|
|
16
19
|
- [User Management](#user-management)
|
|
17
|
-
- [
|
|
18
|
-
- [
|
|
19
|
-
- [Configuration Modes](#configuration-modes)
|
|
20
|
-
- [IAM Integration](#iam-integration)
|
|
20
|
+
- [No-IAM Mode](#no-iam-mode)
|
|
21
|
+
- [API Reference](#api-reference)
|
|
21
22
|
|
|
22
23
|
---
|
|
23
24
|
|
|
@@ -40,17 +41,16 @@ import { AuthModule } from '@flusys/nestjs-auth';
|
|
|
40
41
|
AuthModule.forRoot({
|
|
41
42
|
global: true,
|
|
42
43
|
includeController: true,
|
|
43
|
-
includeIAM: true, // Include IAM entities if using @flusys/nestjs-iam
|
|
44
44
|
bootstrapAppConfig: {
|
|
45
45
|
databaseMode: 'single',
|
|
46
46
|
enableCompanyFeature: false,
|
|
47
47
|
},
|
|
48
48
|
config: {
|
|
49
49
|
defaultDatabaseConfig: {
|
|
50
|
-
type: '
|
|
50
|
+
type: 'postgres',
|
|
51
51
|
host: 'localhost',
|
|
52
|
-
port:
|
|
53
|
-
username: '
|
|
52
|
+
port: 5432,
|
|
53
|
+
username: 'postgres',
|
|
54
54
|
password: 'password',
|
|
55
55
|
database: 'myapp',
|
|
56
56
|
},
|
|
@@ -69,20 +69,12 @@ export class AppModule {}
|
|
|
69
69
|
AuthModule.forRoot({
|
|
70
70
|
global: true,
|
|
71
71
|
includeController: true,
|
|
72
|
-
includeIAM: true, // Include IAM entities if using @flusys/nestjs-iam
|
|
73
72
|
bootstrapAppConfig: {
|
|
74
73
|
databaseMode: 'single',
|
|
75
|
-
enableCompanyFeature: true,
|
|
74
|
+
enableCompanyFeature: true,
|
|
76
75
|
},
|
|
77
76
|
config: {
|
|
78
|
-
defaultDatabaseConfig: {
|
|
79
|
-
type: 'mysql',
|
|
80
|
-
host: 'localhost',
|
|
81
|
-
port: 3306,
|
|
82
|
-
username: 'root',
|
|
83
|
-
password: 'password',
|
|
84
|
-
database: 'myapp',
|
|
85
|
-
},
|
|
77
|
+
defaultDatabaseConfig: { /* ... */ },
|
|
86
78
|
jwtSecret: process.env.JWT_SECRET,
|
|
87
79
|
refreshTokenSecret: process.env.REFRESH_TOKEN_SECRET,
|
|
88
80
|
refreshTokenExpiration: '7d',
|
|
@@ -97,17 +89,16 @@ AuthModule.forRoot({
|
|
|
97
89
|
AuthModule.forRoot({
|
|
98
90
|
global: true,
|
|
99
91
|
includeController: true,
|
|
100
|
-
includeIAM: true, // Include IAM entities if using @flusys/nestjs-iam
|
|
101
92
|
bootstrapAppConfig: {
|
|
102
93
|
databaseMode: 'multi-tenant',
|
|
103
94
|
enableCompanyFeature: true,
|
|
104
95
|
},
|
|
105
96
|
config: {
|
|
106
97
|
tenantDefaultDatabaseConfig: {
|
|
107
|
-
type: '
|
|
98
|
+
type: 'postgres',
|
|
108
99
|
host: 'localhost',
|
|
109
|
-
port:
|
|
110
|
-
username: '
|
|
100
|
+
port: 5432,
|
|
101
|
+
username: 'postgres',
|
|
111
102
|
password: 'password',
|
|
112
103
|
},
|
|
113
104
|
tenants: [
|
|
@@ -119,19 +110,36 @@ AuthModule.forRoot({
|
|
|
119
110
|
})
|
|
120
111
|
```
|
|
121
112
|
|
|
122
|
-
###
|
|
113
|
+
### With Email Provider (Password Reset, Email Verification)
|
|
123
114
|
|
|
124
115
|
```typescript
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
116
|
+
import { AUTH_EMAIL_PROVIDER } from '@flusys/nestjs-auth';
|
|
117
|
+
import { EmailSendService } from '@flusys/nestjs-email';
|
|
118
|
+
|
|
119
|
+
AuthModule.forRoot({
|
|
120
|
+
// ... config
|
|
121
|
+
providers: [
|
|
122
|
+
{
|
|
123
|
+
provide: AUTH_EMAIL_PROVIDER,
|
|
124
|
+
useFactory: (emailService: EmailSendService) => ({
|
|
125
|
+
sendPasswordResetEmail: async (email, token, resetUrl) => {
|
|
126
|
+
await emailService.sendTemplateEmail({
|
|
127
|
+
templateSlug: 'password-reset',
|
|
128
|
+
to: email,
|
|
129
|
+
variables: { resetUrl, token },
|
|
130
|
+
});
|
|
131
|
+
},
|
|
132
|
+
sendVerificationEmail: async (email, token, verifyUrl) => {
|
|
133
|
+
await emailService.sendTemplateEmail({
|
|
134
|
+
templateSlug: 'email-verification',
|
|
135
|
+
to: email,
|
|
136
|
+
variables: { verifyUrl, token },
|
|
137
|
+
});
|
|
138
|
+
},
|
|
139
|
+
}),
|
|
140
|
+
inject: [EmailSendService],
|
|
141
|
+
},
|
|
142
|
+
],
|
|
135
143
|
})
|
|
136
144
|
```
|
|
137
145
|
|
|
@@ -143,356 +151,425 @@ AuthModule.forRootAsync({
|
|
|
143
151
|
interface AuthModuleOptions {
|
|
144
152
|
global?: boolean; // Make module global
|
|
145
153
|
includeController?: boolean; // Include default controllers
|
|
146
|
-
|
|
147
|
-
bootstrapAppConfig: {
|
|
154
|
+
bootstrapAppConfig?: {
|
|
148
155
|
databaseMode: 'single' | 'multi-tenant';
|
|
149
|
-
enableCompanyFeature: boolean;
|
|
156
|
+
enableCompanyFeature: boolean;
|
|
157
|
+
enableEmailVerification?: boolean; // Default: true
|
|
150
158
|
};
|
|
151
|
-
config
|
|
159
|
+
config?: {
|
|
152
160
|
defaultDatabaseConfig?: IDatabaseConfig;
|
|
153
161
|
tenantDefaultDatabaseConfig?: IDatabaseConfig;
|
|
154
162
|
tenants?: ITenantDatabaseConfig[];
|
|
155
|
-
jwtSecret
|
|
156
|
-
jwtExpiration?: string;
|
|
163
|
+
jwtSecret?: string;
|
|
164
|
+
jwtExpiration?: string; // Default: '1h'
|
|
157
165
|
refreshTokenSecret?: string;
|
|
158
|
-
refreshTokenExpiration?: string;
|
|
159
|
-
refreshTokenCookieName?: string;
|
|
166
|
+
refreshTokenExpiration?: string; // Default: '7d'
|
|
167
|
+
refreshTokenCookieName?: string; // Default: 'fsn_refresh_token'
|
|
168
|
+
frontendUrl?: string; // For email links
|
|
160
169
|
};
|
|
170
|
+
providers?: Provider[]; // Additional providers (AUTH_EMAIL_PROVIDER, USER_ENRICHER)
|
|
161
171
|
}
|
|
162
172
|
```
|
|
163
173
|
|
|
164
|
-
**
|
|
174
|
+
**Bootstrap Configuration Flags:**
|
|
165
175
|
|
|
166
|
-
|
|
176
|
+
| Flag | Type | Default | Effect |
|
|
177
|
+
|------|------|---------|--------|
|
|
178
|
+
| `enableCompanyFeature` | `boolean` | `false` | Enables company/branch entities and APIs |
|
|
179
|
+
| `enableEmailVerification` | `boolean` | `true` | When `false`: excludes `AuthEmailService`, hides email-related APIs (forgot-password, reset-password, verify-email, resend-verification), and excludes `PendingRegistration` entity from migrations |
|
|
167
180
|
|
|
168
|
-
|
|
181
|
+
## Constants
|
|
169
182
|
|
|
170
|
-
|
|
183
|
+
```typescript
|
|
184
|
+
// Injection Tokens
|
|
185
|
+
export const AUTH_MODULE_OPTIONS = 'AUTH_MODULE_OPTIONS';
|
|
186
|
+
export const USER_ENRICHER = 'USER_ENRICHER';
|
|
171
187
|
|
|
172
|
-
|
|
188
|
+
// Security
|
|
189
|
+
export const BCRYPT_SALT_ROUNDS = 12;
|
|
173
190
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
| `/auth/register` | POST | Register new user |
|
|
178
|
-
| `/auth/refresh` | POST | Refresh access token |
|
|
179
|
-
| `/auth/logout` | POST | Logout (clears refresh token cookie) |
|
|
180
|
-
| `/auth/change-password` | POST | Change user password |
|
|
181
|
-
| `/auth/me` | GET | Get current user info |
|
|
191
|
+
// Cookie
|
|
192
|
+
export const DEFAULT_COOKIE_NAME = 'fsn_refresh_token';
|
|
193
|
+
export const MAX_COOKIE_BYTES = 3500;
|
|
182
194
|
|
|
183
|
-
|
|
195
|
+
// Cache Key Prefixes
|
|
196
|
+
export const SESSION_CACHE_PREFIX = 'auth:session';
|
|
197
|
+
export const TOKEN_REVOKED_PREFIX = 'auth:revoked';
|
|
198
|
+
```
|
|
184
199
|
|
|
185
|
-
|
|
200
|
+
## Entities
|
|
186
201
|
|
|
187
|
-
|
|
202
|
+
### Entity Groups
|
|
188
203
|
|
|
189
|
-
|
|
204
|
+
The auth module organizes entities into groups based on feature flags:
|
|
190
205
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
206
|
+
```typescript
|
|
207
|
+
// Internal entity groups (not exported directly)
|
|
208
|
+
// Base entities: User, UserToken
|
|
209
|
+
// Email entities: PendingRegistration
|
|
210
|
+
// Company entities: Company, CompanyBranch, UserCompanyPermission
|
|
211
|
+
|
|
212
|
+
// Exported interface for configuration
|
|
213
|
+
export interface IAuthEntitiesConfig {
|
|
214
|
+
enableCompanyFeature?: boolean;
|
|
215
|
+
enableEmailVerification?: boolean; // Default: true
|
|
200
216
|
}
|
|
201
|
-
```
|
|
202
217
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
{
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
},
|
|
212
|
-
"company": {
|
|
213
|
-
"id": "550e8400-e29b-41d4-a716-446655440001",
|
|
214
|
-
"name": "Acme Corporation",
|
|
215
|
-
"slug": "acme-corp",
|
|
216
|
-
"logoId": null
|
|
217
|
-
},
|
|
218
|
-
"branch": {
|
|
219
|
-
"id": "550e8400-e29b-41d4-a716-446655440002",
|
|
220
|
-
"name": "Main Branch",
|
|
221
|
-
"slug": "main-branch",
|
|
222
|
-
"logoId": null
|
|
223
|
-
}
|
|
218
|
+
// Get entities based on feature flags (exported)
|
|
219
|
+
export function getEntitiesByConfig(config: IAuthEntitiesConfig): any[] {
|
|
220
|
+
const { enableCompanyFeature = false, enableEmailVerification = true } = config;
|
|
221
|
+
|
|
222
|
+
const entities = [User, UserToken];
|
|
223
|
+
if (enableEmailVerification) entities.push(PendingRegistration);
|
|
224
|
+
if (enableCompanyFeature) entities.push(Company, CompanyBranch, UserCompanyPermission);
|
|
225
|
+
return entities;
|
|
224
226
|
}
|
|
225
227
|
```
|
|
226
228
|
|
|
227
|
-
**
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
- `company` and `branch` fields are automatically excluded from Swagger docs when company feature is disabled
|
|
231
|
-
- Used for session restoration on page refresh (frontend calls this after token refresh)
|
|
229
|
+
**Usage:**
|
|
230
|
+
```typescript
|
|
231
|
+
import { getEntitiesByConfig } from '@flusys/nestjs-auth/entities';
|
|
232
232
|
|
|
233
|
-
|
|
233
|
+
// Get entities for single database without company feature
|
|
234
|
+
const entities = getEntitiesByConfig({ enableCompanyFeature: false });
|
|
235
|
+
// Returns: [User, UserToken, PendingRegistration]
|
|
234
236
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
| `/auth/select` | POST | Select company and branch after login |
|
|
240
|
-
| `/auth/switch-company` | POST | Switch to different company/branch |
|
|
241
|
-
| `/auth/companies` | GET | Get user's available companies |
|
|
242
|
-
| `/auth/companies/:companyId/branches` | GET | Get branches for a company |
|
|
237
|
+
// Get entities with company feature, no email verification
|
|
238
|
+
const entities = getEntitiesByConfig({ enableCompanyFeature: true, enableEmailVerification: false });
|
|
239
|
+
// Returns: [User, UserToken, Company, CompanyBranch, UserCompanyPermission]
|
|
240
|
+
```
|
|
243
241
|
|
|
244
|
-
|
|
242
|
+
**Entity Selection by Feature Flags:**
|
|
245
243
|
|
|
246
|
-
|
|
244
|
+
| enableEmailVerification | enableCompanyFeature | Entities |
|
|
245
|
+
|------------------------|---------------------|----------|
|
|
246
|
+
| `true` (default) | `false` | User, UserToken, PendingRegistration |
|
|
247
|
+
| `true` | `true` | All entities |
|
|
248
|
+
| `false` | `false` | User, UserToken only |
|
|
249
|
+
| `false` | `true` | User, UserToken, Company, CompanyBranch, UserCompanyPermission |
|
|
247
250
|
|
|
248
|
-
|
|
251
|
+
### User Entity
|
|
249
252
|
|
|
250
|
-
|
|
251
|
-
- Companies
|
|
252
|
-
- Branches
|
|
253
|
-
- User Permissions (NEW - v4.1.3+)
|
|
254
|
-
- Company Selection
|
|
253
|
+
Extends `UserRoot` from `@flusys/nestjs-shared`:
|
|
255
254
|
|
|
256
|
-
|
|
255
|
+
| Field | Type | Description |
|
|
256
|
+
|-------|------|-------------|
|
|
257
|
+
| `id` | `uuid` | Primary key |
|
|
258
|
+
| `name` | `varchar(255)` | User's name |
|
|
259
|
+
| `email` | `varchar(255)` | Unique email address |
|
|
260
|
+
| `password` | `text` | Bcrypt hashed password |
|
|
261
|
+
| `phone` | `varchar(255)` | Phone number |
|
|
262
|
+
| `isActive` | `boolean` | Account status |
|
|
263
|
+
| `emailVerified` | `boolean` | Email verification status |
|
|
264
|
+
| `phoneVerified` | `boolean` | Phone verification status |
|
|
265
|
+
| `profilePictureId` | `uuid` | Reference to storage file |
|
|
266
|
+
| `lastLoginAt` | `timestamp` | Last login timestamp |
|
|
267
|
+
| `additionalFields` | `json` | Extensible JSON fields |
|
|
268
|
+
| `createdAt` | `timestamp` | Auto-generated |
|
|
269
|
+
| `updatedAt` | `timestamp` | Auto-updated |
|
|
270
|
+
| `deletedAt` | `timestamp` | Soft delete timestamp |
|
|
257
271
|
|
|
258
|
-
|
|
259
|
-
|-----|---------------|
|
|
260
|
-
| `RegistrationDto` | `companySlug`, `branchSlug`, `newCompanyName`, `newCompanyPhone`, `newCompanyAddress` |
|
|
261
|
-
| `RegistrationResponseDto` | `company`, `branch`, `companyFeatureEnabled` |
|
|
262
|
-
| `LoginResponseDto` | `company`, `branch`, `companyFeatureEnabled` |
|
|
263
|
-
| `MeResponseDto` | `company`, `branch` |
|
|
272
|
+
### UserToken Entity
|
|
264
273
|
|
|
265
|
-
|
|
274
|
+
Stores tokens for existing user actions:
|
|
266
275
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
| `/users/get-all` | POST | Get paginated user list |
|
|
273
|
-
| `/users/update` | POST | Update user |
|
|
274
|
-
| `/users/update-many` | POST | Bulk update users |
|
|
275
|
-
| `/users/delete` | POST | Delete/restore/permanent delete |
|
|
276
|
-
| `/users/profile` | POST | Update own profile |
|
|
277
|
-
| `/users/:id/verify-email` | POST | Mark email as verified |
|
|
278
|
-
| `/users/:id/verify-phone` | POST | Mark phone as verified |
|
|
279
|
-
| `/users/:id/status` | PUT | Update user active status |
|
|
280
|
-
|
|
281
|
-
### User Permission Endpoints (NEW - when company feature enabled)
|
|
282
|
-
|
|
283
|
-
| Endpoint | Method | Description |
|
|
284
|
-
|----------|--------|-------------|
|
|
285
|
-
| `/administration/permissions/user-company/assign` | POST | Batch assign/revoke user-company permissions |
|
|
286
|
-
| `/administration/permissions/user-company` | GET | Get user's assigned companies |
|
|
287
|
-
| `/administration/permissions/user-branch/assign` | POST | Batch assign/revoke user-branch permissions |
|
|
288
|
-
| `/administration/permissions/user-branch` | GET | Get user's assigned branches |
|
|
289
|
-
|
|
290
|
-
**Example: Assign companies to user**
|
|
291
|
-
```json
|
|
292
|
-
POST /administration/permissions/user-company/assign
|
|
293
|
-
{
|
|
294
|
-
"userId": "user-123",
|
|
295
|
-
"items": [
|
|
296
|
-
{ "targetId": "company-1", "isAdd": true },
|
|
297
|
-
{ "targetId": "company-2", "isAdd": true }
|
|
298
|
-
]
|
|
276
|
+
```typescript
|
|
277
|
+
enum TokenType {
|
|
278
|
+
PASSWORD_RESET = 'password_reset',
|
|
279
|
+
EMAIL_VERIFICATION = 'email_verification',
|
|
280
|
+
EMAIL_CHANGE = 'email_change',
|
|
299
281
|
}
|
|
300
282
|
```
|
|
301
283
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
284
|
+
| Field | Type | Description |
|
|
285
|
+
|-------|------|-------------|
|
|
286
|
+
| `id` | `uuid` | Primary key |
|
|
287
|
+
| `userId` | `uuid` | Reference to user |
|
|
288
|
+
| `type` | `TokenType` | Token purpose |
|
|
289
|
+
| `token` | `varchar(64)` | SHA-256 hashed token |
|
|
290
|
+
| `expiresAt` | `timestamp` | Expiration time |
|
|
291
|
+
| `usedAt` | `timestamp` | When token was used |
|
|
292
|
+
| `metadata` | `varchar(255)` | Additional data (e.g., new email for EMAIL_CHANGE) |
|
|
293
|
+
| `createdAt` | `timestamp` | Auto-generated |
|
|
294
|
+
|
|
295
|
+
**Indexes:**
|
|
296
|
+
- `IDX_user_token_token` - Unique on token
|
|
297
|
+
- `IDX_user_token_user_type` - userId + type
|
|
298
|
+
- `IDX_user_token_expires` - For cleanup queries
|
|
299
|
+
|
|
300
|
+
### PendingRegistration Entity
|
|
301
|
+
|
|
302
|
+
Stores registration data until email verification (prevents garbage accounts):
|
|
303
|
+
|
|
304
|
+
| Field | Type | Description |
|
|
305
|
+
|-------|------|-------------|
|
|
306
|
+
| `id` | `uuid` | Primary key |
|
|
307
|
+
| `email` | `varchar(255)` | Unique pending email |
|
|
308
|
+
| `name` | `varchar(255)` | User's name |
|
|
309
|
+
| `phone` | `varchar(255)` | Phone number |
|
|
310
|
+
| `passwordHash` | `text` | Pre-hashed password |
|
|
311
|
+
| `token` | `varchar(64)` | SHA-256 hashed verification token |
|
|
312
|
+
| `expiresAt` | `timestamp` | Expiration (24 hours) |
|
|
313
|
+
| `companyData` | `json` | Company registration data |
|
|
314
|
+
| `createdAt` | `timestamp` | Auto-generated |
|
|
315
|
+
|
|
316
|
+
**Indexes:**
|
|
317
|
+
- `IDX_pending_reg_email` - Unique
|
|
318
|
+
- `IDX_pending_reg_token` - Unique
|
|
319
|
+
- `IDX_pending_reg_expires` - For cleanup
|
|
320
|
+
|
|
321
|
+
### Company Entity (when company feature enabled)
|
|
322
|
+
|
|
323
|
+
| Field | Type | Description |
|
|
324
|
+
|-------|------|-------------|
|
|
325
|
+
| `id` | `uuid` | Primary key |
|
|
326
|
+
| `name` | `varchar(255)` | Company name |
|
|
327
|
+
| `slug` | `varchar(255)` | URL-friendly identifier |
|
|
328
|
+
| `phone` | `varchar(255)` | Contact phone |
|
|
329
|
+
| `address` | `text` | Address |
|
|
330
|
+
| `logoId` | `uuid` | Reference to storage file |
|
|
331
|
+
| `isActive` | `boolean` | Company status |
|
|
332
|
+
|
|
333
|
+
### CompanyBranch Entity
|
|
334
|
+
|
|
335
|
+
| Field | Type | Description |
|
|
336
|
+
|-------|------|-------------|
|
|
337
|
+
| `id` | `uuid` | Primary key |
|
|
338
|
+
| `name` | `varchar(255)` | Branch name |
|
|
339
|
+
| `slug` | `varchar(255)` | URL-friendly identifier |
|
|
340
|
+
| `companyId` | `uuid` | Parent company |
|
|
341
|
+
| `parentId` | `uuid` | Parent branch (hierarchical) |
|
|
342
|
+
| `serial` | `int` | Display order |
|
|
343
|
+
| `logoId` | `uuid` | Reference to storage file |
|
|
344
|
+
| `isActive` | `boolean` | Branch status |
|
|
345
|
+
|
|
346
|
+
### UserCompanyPermission Entity (Polymorphic)
|
|
347
|
+
|
|
348
|
+
```typescript
|
|
349
|
+
enum PermissionType {
|
|
350
|
+
COMPANY = 'company',
|
|
351
|
+
BRANCH = 'branch',
|
|
310
352
|
}
|
|
311
353
|
```
|
|
312
354
|
|
|
313
|
-
|
|
355
|
+
| Field | Type | Description |
|
|
356
|
+
|-------|------|-------------|
|
|
357
|
+
| `id` | `uuid` | Primary key |
|
|
358
|
+
| `userId` | `uuid` | Reference to user |
|
|
359
|
+
| `permissionType` | `PermissionType` | company or branch |
|
|
360
|
+
| `targetId` | `uuid` | Company ID or Branch ID |
|
|
361
|
+
| `isActive` | `boolean` | Permission status |
|
|
314
362
|
|
|
315
|
-
|
|
363
|
+
## Provider Interfaces
|
|
316
364
|
|
|
317
|
-
|
|
318
|
-
|----------|--------|-------------|
|
|
319
|
-
| `/company/insert` | POST | Create company |
|
|
320
|
-
| `/company/insert-many` | POST | Bulk create companies |
|
|
321
|
-
| `/company/get/:id` | POST | Get company by ID |
|
|
322
|
-
| `/company/get-all` | POST | Get paginated company list |
|
|
323
|
-
| `/company/update` | POST | Update company |
|
|
324
|
-
| `/company/update-many` | POST | Bulk update companies |
|
|
325
|
-
| `/company/delete` | POST | Delete/restore company |
|
|
365
|
+
### AUTH_EMAIL_PROVIDER
|
|
326
366
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
| Endpoint | Method | Description |
|
|
330
|
-
|----------|--------|-------------|
|
|
331
|
-
| `/branch/insert` | POST | Create branch |
|
|
332
|
-
| `/branch/insert-many` | POST | Bulk create branches |
|
|
333
|
-
| `/branch/get/:id` | POST | Get branch by ID |
|
|
334
|
-
| `/branch/get-all` | POST | Get paginated branch list (auto-filtered by company) |
|
|
335
|
-
| `/branch/update` | POST | Update branch |
|
|
336
|
-
| `/branch/update-many` | POST | Bulk update branches |
|
|
337
|
-
| `/branch/delete` | POST | Delete/restore branch |
|
|
367
|
+
Allows auth to work independently of the email module:
|
|
338
368
|
|
|
339
|
-
|
|
369
|
+
```typescript
|
|
370
|
+
interface IAuthEmailProvider {
|
|
371
|
+
/** Send password reset email */
|
|
372
|
+
sendPasswordResetEmail(email: string, token: string, resetUrl: string): Promise<void>;
|
|
340
373
|
|
|
341
|
-
|
|
374
|
+
/** Send email verification email */
|
|
375
|
+
sendVerificationEmail(email: string, token: string, verifyUrl: string): Promise<void>;
|
|
342
376
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
└── PermissionGuard (checks permissions from cache)
|
|
347
|
-
↓
|
|
348
|
-
@flusys/nestjs-auth
|
|
349
|
-
├── AuthConfigService (JWT settings via AUTH_MODULE_OPTIONS)
|
|
350
|
-
├── JwtStrategy (registers with Passport, validates users)
|
|
351
|
-
├── AuthenticationService (generates tokens)
|
|
352
|
-
├── User management
|
|
353
|
-
└── Company/Branch management (optional)
|
|
377
|
+
/** Send welcome email after registration (optional) */
|
|
378
|
+
sendWelcomeEmail?(email: string, name: string): Promise<void>;
|
|
379
|
+
}
|
|
354
380
|
```
|
|
355
381
|
|
|
356
|
-
**
|
|
357
|
-
- ✅ **JwtAuthGuard** in shared → All modules can use without depending on Auth
|
|
358
|
-
- ✅ **JwtStrategy** in auth → Registers JWT validation with Passport
|
|
359
|
-
- ✅ **AuthConfigService** in auth → Provides JWT settings (secret, expiration)
|
|
360
|
-
- ✅ **Token generation** handled by AuthenticationService
|
|
361
|
-
- ✅ **Token validation** uses JwtStrategy registered by Auth module
|
|
382
|
+
**Example Implementation:**
|
|
362
383
|
|
|
363
|
-
|
|
384
|
+
```typescript
|
|
385
|
+
{
|
|
386
|
+
provide: AUTH_EMAIL_PROVIDER,
|
|
387
|
+
useFactory: (emailSendService: EmailSendService) => ({
|
|
388
|
+
sendPasswordResetEmail: async (email, token, resetUrl) => {
|
|
389
|
+
await emailSendService.sendTemplateEmail({
|
|
390
|
+
templateSlug: 'password-reset',
|
|
391
|
+
to: email,
|
|
392
|
+
variables: { resetUrl, token },
|
|
393
|
+
});
|
|
394
|
+
},
|
|
395
|
+
sendVerificationEmail: async (email, token, verifyUrl) => {
|
|
396
|
+
await emailSendService.sendTemplateEmail({
|
|
397
|
+
templateSlug: 'email-verification',
|
|
398
|
+
to: email,
|
|
399
|
+
variables: { verifyUrl, token },
|
|
400
|
+
});
|
|
401
|
+
},
|
|
402
|
+
}),
|
|
403
|
+
inject: [EmailSendService],
|
|
404
|
+
}
|
|
405
|
+
```
|
|
364
406
|
|
|
365
|
-
###
|
|
407
|
+
### USER_ENRICHER
|
|
366
408
|
|
|
367
|
-
|
|
409
|
+
Extends user list and profile without modifying base package:
|
|
368
410
|
|
|
369
411
|
```typescript
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
) {
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
412
|
+
interface IUserEnricher {
|
|
413
|
+
/** Enrich user list query with extra joins/selects */
|
|
414
|
+
enrichListQuery?(
|
|
415
|
+
query: SelectQueryBuilder<User>,
|
|
416
|
+
user: ILoggedUserInfo | null,
|
|
417
|
+
): Promise<{ extraFields: string[] }>;
|
|
418
|
+
|
|
419
|
+
/** Transform user list items with extra computed data */
|
|
420
|
+
enrichListItems?(
|
|
421
|
+
users: IUser[],
|
|
422
|
+
user: ILoggedUserInfo | null,
|
|
423
|
+
): Promise<IEnrichedUser[]>;
|
|
424
|
+
|
|
425
|
+
/** Get extra profile data (roles, permissions, sections) */
|
|
426
|
+
getProfileExtras?(
|
|
427
|
+
userId: string,
|
|
428
|
+
user: ILoggedUserInfo,
|
|
429
|
+
): Promise<IProfileExtras>;
|
|
430
|
+
|
|
431
|
+
/** Handle profile update with extra fields */
|
|
432
|
+
updateProfileExtras?(
|
|
433
|
+
userId: string,
|
|
434
|
+
extras: Record<string, any>,
|
|
435
|
+
queryRunner: QueryRunner,
|
|
436
|
+
): Promise<void>;
|
|
437
|
+
|
|
438
|
+
/** Validate extra fields before profile update */
|
|
439
|
+
validateProfileExtras?(
|
|
440
|
+
userId: string,
|
|
441
|
+
extras: Record<string, any>,
|
|
442
|
+
user: ILoggedUserInfo,
|
|
443
|
+
): Promise<void>;
|
|
444
|
+
|
|
445
|
+
// Multi-Step Profile Methods (LEADPRO-style)
|
|
446
|
+
getProfileSections?(): IProfileSection[];
|
|
447
|
+
getProfileSectionData?(userId, sectionId, user): Promise<IProfileSectionData>;
|
|
448
|
+
updateProfileSection?(userId, sectionId, data, queryRunner): Promise<void>;
|
|
449
|
+
handleSectionFileUpload?(userId, sectionId, field, fileInfo, queryRunner): Promise<IProfileFile>;
|
|
450
|
+
handleSectionFileDelete?(userId, sectionId, field, fileId, queryRunner): Promise<void>;
|
|
451
|
+
calculateProfileCompletion?(userId, user): Promise<number>;
|
|
452
|
+
}
|
|
453
|
+
```
|
|
382
454
|
|
|
383
|
-
|
|
384
|
-
// Resolve request-scoped AuthDataSourceProvider per request
|
|
385
|
-
const dataSourceProvider = await this.moduleRef.resolve(
|
|
386
|
-
AuthDataSourceProvider,
|
|
387
|
-
undefined,
|
|
388
|
-
{ strict: false },
|
|
389
|
-
);
|
|
455
|
+
**Profile Section Interface (for multi-step profiles):**
|
|
390
456
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
457
|
+
```typescript
|
|
458
|
+
interface IProfileSection {
|
|
459
|
+
id: string; // Unique identifier
|
|
460
|
+
name: string; // Display name
|
|
461
|
+
order: number; // Stepper order
|
|
462
|
+
icon?: string; // PrimeNG icon class
|
|
463
|
+
required?: boolean; // Required for completion
|
|
464
|
+
hasFileUpload?: boolean; // Has file fields
|
|
465
|
+
editPermission?: string; // Required permission
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
interface IProfileExtras {
|
|
469
|
+
roles?: IProfileRole[];
|
|
470
|
+
actions?: IProfileAction[];
|
|
471
|
+
sections?: IProfileSectionData[];
|
|
472
|
+
completionPercentage?: number;
|
|
394
473
|
}
|
|
395
474
|
```
|
|
396
475
|
|
|
397
|
-
|
|
398
|
-
- ✅ Works for both **single-tenant** and **multi-tenant** modes
|
|
399
|
-
- ✅ `AuthDataSourceProvider` handles tenant detection from request headers
|
|
400
|
-
- ✅ Connection pools are cached internally (no reconnect per request)
|
|
401
|
-
- ✅ Singleton strategy + request-scoped provider = best of both worlds
|
|
402
|
-
|
|
403
|
-
### Package Structure
|
|
404
|
-
|
|
405
|
-
```
|
|
406
|
-
┌─────────────────────────────────────────────────────────┐
|
|
407
|
-
│ @flusys/nestjs-auth v4.1.2+ │
|
|
408
|
-
│ │
|
|
409
|
-
│ ┌─────────────────────────────────────────────────┐ │
|
|
410
|
-
│ │ CORE AUTH (Always Loaded) │ │
|
|
411
|
-
│ │ │ │
|
|
412
|
-
│ │ ┌──────────┐ │ │
|
|
413
|
-
│ │ │ User │ │ │
|
|
414
|
-
│ │ └──────────┘ │ │
|
|
415
|
-
│ │ │ │
|
|
416
|
-
│ │ • User login/registration │ │
|
|
417
|
-
│ │ • Password hashing (bcrypt) │ │
|
|
418
|
-
│ │ • JWT token generation │ │
|
|
419
|
-
│ │ • JwtStrategy (Passport integration) │ │
|
|
420
|
-
│ └─────────────────────────────────────────────────┘ │
|
|
421
|
-
│ │
|
|
422
|
-
│ ┌─────────────────────────────────────────────────┐ │
|
|
423
|
-
│ │ COMPANY FEATURE (Optional - Flag Enabled) │ │
|
|
424
|
-
│ │ │ │
|
|
425
|
-
│ │ ┌─────────┐ ┌──────────────┐ ┌────────────┐ │ │
|
|
426
|
-
│ │ │ Company │ │CompanyBranch │ │UserCompany │ │ │
|
|
427
|
-
│ │ │ │ │ │ │Permission │ │ │
|
|
428
|
-
│ │ └─────────┘ └──────────────┘ └────────────┘ │ │
|
|
429
|
-
│ │ │ │
|
|
430
|
-
│ │ • Multi-company support │ │
|
|
431
|
-
│ │ • Hierarchical branches │ │
|
|
432
|
-
│ │ • User-company-branch assignments │ │
|
|
433
|
-
│ │ • Company-scoped permissions │ │
|
|
434
|
-
│ │ • Enhanced login with selection │ │
|
|
435
|
-
│ └─────────────────────────────────────────────────┘ │
|
|
436
|
-
└─────────────────────────────────────────────────────────┘
|
|
437
|
-
```
|
|
438
|
-
|
|
439
|
-
### Entity Relationships
|
|
440
|
-
|
|
441
|
-
#### Without Company Feature (Simple)
|
|
442
|
-
|
|
443
|
-
```
|
|
444
|
-
┌──────────────┐
|
|
445
|
-
│ User │
|
|
446
|
-
├──────────────┤
|
|
447
|
-
│ id │
|
|
448
|
-
│ name │
|
|
449
|
-
│ email │◄────── Authentication
|
|
450
|
-
│ password │
|
|
451
|
-
│ phone │
|
|
452
|
-
└──────────────┘
|
|
453
|
-
```
|
|
454
|
-
|
|
455
|
-
#### With Company Feature (Polymorphic Design)
|
|
456
|
-
|
|
457
|
-
```
|
|
458
|
-
┌──────────────┐ ┌───────────────────┐ ┌──────────────────┐
|
|
459
|
-
│ User │ │ UserCompany │ │ Company │
|
|
460
|
-
├──────────────┤ │ Permission │ ├──────────────────┤
|
|
461
|
-
│ id │◄──────┤ userId │ │ id │
|
|
462
|
-
│ name │ │ permissionType │──────►│ name │
|
|
463
|
-
│ email │ │ targetId (UUID) │ │ slug │
|
|
464
|
-
│ password │ │ isActive │ │ isActive │
|
|
465
|
-
└──────────────┘ │ metadata │ └──────────────────┘
|
|
466
|
-
└───────────────────┘ │
|
|
467
|
-
│ │
|
|
468
|
-
permissionType: 'company'|'branch' ▼
|
|
469
|
-
targetId: company.id OR branch.id ┌──────────────────┐
|
|
470
|
-
│ │ CompanyBranch │
|
|
471
|
-
└──────────────────►├──────────────────┤
|
|
472
|
-
│ id │
|
|
473
|
-
│ name, companyId │
|
|
474
|
-
│ parentId ◄──┐ │
|
|
475
|
-
│ isActive │ │
|
|
476
|
-
└─────────────┴────┘
|
|
477
|
-
Hierarchical
|
|
478
|
-
```
|
|
479
|
-
|
|
480
|
-
**Polymorphic Permission Design:**
|
|
481
|
-
- `permissionType`: Enum ('company' | 'branch') determines target type
|
|
482
|
-
- `targetId`: UUID of the company OR branch (single field, not both)
|
|
483
|
-
- One table handles both company and branch permissions
|
|
484
|
-
|
|
485
|
-
### Services
|
|
476
|
+
## Services
|
|
486
477
|
|
|
487
478
|
| Service | Scope | Description |
|
|
488
479
|
|---------|-------|-------------|
|
|
489
480
|
| `AuthenticationService` | REQUEST | Login, register, token management, company selection |
|
|
481
|
+
| `AuthEmailService` | REQUEST | Email verification, password reset, pending registrations |
|
|
490
482
|
| `UserService` | REQUEST | User CRUD, extends `RequestScopedApiService` |
|
|
491
483
|
| `CompanyService` | REQUEST | Company CRUD (when company feature enabled) |
|
|
492
484
|
| `BranchService` | REQUEST | Branch CRUD with company filtering |
|
|
493
485
|
| `UserPermissionService` | REQUEST | User-company/branch permission management |
|
|
494
486
|
| `AuthDataSourceProvider` | REQUEST | Dynamic datasource for single/multi-tenant |
|
|
495
487
|
| `AuthConfigService` | SINGLETON | JWT and module configuration access |
|
|
488
|
+
| `CompanySelectionSessionService` | SINGLETON | Session management for company selection |
|
|
489
|
+
|
|
490
|
+
### AuthEmailService
|
|
491
|
+
|
|
492
|
+
Token Security: All tokens are stored as SHA-256 hashes. Raw tokens are sent to users via email, but only hashes are stored in the database.
|
|
493
|
+
|
|
494
|
+
| Method | Description |
|
|
495
|
+
|--------|-------------|
|
|
496
|
+
| `isEmailEnabled()` | Check if email provider is configured |
|
|
497
|
+
| `hasPendingRegistration(email)` | Check for pending registration |
|
|
498
|
+
| `createPendingRegistration(data)` | Create pending registration with verification email |
|
|
499
|
+
| `forgotPassword(email, resetUrlBase)` | Send password reset email |
|
|
500
|
+
| `resetPassword(token, newPassword)` | Reset password using token |
|
|
501
|
+
| `verifyEmail(token)` | Verify email (pending registration or existing user) |
|
|
502
|
+
| `resendVerificationEmail(email, verifyUrlBase)` | Resend verification email |
|
|
503
|
+
| `cleanupExpired()` | Clean up expired tokens and pending registrations |
|
|
504
|
+
|
|
505
|
+
**Token Expiry:**
|
|
506
|
+
- Password Reset: 1 hour
|
|
507
|
+
- Email Verification: 24 hours
|
|
508
|
+
|
|
509
|
+
## API Endpoints
|
|
510
|
+
|
|
511
|
+
### Authentication Endpoints
|
|
512
|
+
|
|
513
|
+
| Endpoint | Method | Description |
|
|
514
|
+
|----------|--------|-------------|
|
|
515
|
+
| `/auth/login` | POST | Login with email/password |
|
|
516
|
+
| `/auth/register` | POST | Register new user |
|
|
517
|
+
| `/auth/refresh` | POST | Refresh access token |
|
|
518
|
+
| `/auth/logout` | POST | Logout (revokes all tokens) |
|
|
519
|
+
| `/auth/change-password` | POST | Change user password |
|
|
520
|
+
| `/auth/me` | GET | Get current user info |
|
|
521
|
+
|
|
522
|
+
### Email Authentication Endpoints
|
|
523
|
+
|
|
524
|
+
| Endpoint | Method | Description |
|
|
525
|
+
|----------|--------|-------------|
|
|
526
|
+
| `/auth/forgot-password` | POST | Request password reset email |
|
|
527
|
+
| `/auth/reset-password` | POST | Reset password using token |
|
|
528
|
+
| `/auth/verify-email` | POST | Verify email using token |
|
|
529
|
+
| `/auth/resend-verification` | POST | Resend email verification link |
|
|
530
|
+
|
|
531
|
+
> **Note:** Email endpoints require `AUTH_EMAIL_PROVIDER` to be configured.
|
|
532
|
+
|
|
533
|
+
### Company Selection Endpoints (when company feature enabled)
|
|
534
|
+
|
|
535
|
+
| Endpoint | Method | Description |
|
|
536
|
+
|----------|--------|-------------|
|
|
537
|
+
| `/auth/select` | POST | Select company and branch after login |
|
|
538
|
+
| `/auth/switch-company` | POST | Switch to different company/branch |
|
|
539
|
+
| `/auth/companies` | GET | Get user's available companies |
|
|
540
|
+
| `/auth/companies/:companyId/branches` | GET | Get branches for a company |
|
|
541
|
+
|
|
542
|
+
### User Management Endpoints
|
|
543
|
+
|
|
544
|
+
| Endpoint | Method | Description |
|
|
545
|
+
|----------|--------|-------------|
|
|
546
|
+
| `/administration/users/insert` | POST | Create user |
|
|
547
|
+
| `/administration/users/insert-many` | POST | Bulk create users |
|
|
548
|
+
| `/administration/users/get/:id` | POST | Get user by ID |
|
|
549
|
+
| `/administration/users/get-all` | POST | Get paginated user list |
|
|
550
|
+
| `/administration/users/update` | POST | Update user |
|
|
551
|
+
| `/administration/users/update-many` | POST | Bulk update users |
|
|
552
|
+
| `/administration/users/delete` | POST | Delete/restore/permanent delete |
|
|
553
|
+
| `/administration/users/profile` | POST | Update own profile |
|
|
554
|
+
| `/administration/users/:id/verify-email` | POST | Mark email as verified |
|
|
555
|
+
| `/administration/users/:id/verify-phone` | POST | Mark phone as verified |
|
|
556
|
+
| `/administration/users/:id/status` | PUT | Update user active status |
|
|
557
|
+
|
|
558
|
+
### User Permission Endpoints (when company feature enabled)
|
|
559
|
+
|
|
560
|
+
| Endpoint | Method | Description |
|
|
561
|
+
|----------|--------|-------------|
|
|
562
|
+
| `/administration/permissions/user-company/assign` | POST | Batch assign/revoke user-company permissions |
|
|
563
|
+
| `/administration/permissions/user-company` | GET | Get user's assigned companies |
|
|
564
|
+
| `/administration/permissions/user-branch/assign` | POST | Batch assign/revoke user-branch permissions |
|
|
565
|
+
| `/administration/permissions/user-branch` | GET | Get user's assigned branches |
|
|
566
|
+
|
|
567
|
+
### Company/Branch Management Endpoints (when company feature enabled)
|
|
568
|
+
|
|
569
|
+
| Resource | Endpoints |
|
|
570
|
+
|----------|-----------|
|
|
571
|
+
| Companies | `/administration/company` - CRUD operations |
|
|
572
|
+
| Branches | `/administration/branch` - CRUD operations |
|
|
496
573
|
|
|
497
574
|
## Login Flows
|
|
498
575
|
|
|
@@ -507,8 +584,10 @@ POST /auth/login { email, password }
|
|
|
507
584
|
│ .login() │
|
|
508
585
|
└────────────┬────────────┘
|
|
509
586
|
│
|
|
510
|
-
│ 1.
|
|
511
|
-
│ 2.
|
|
587
|
+
│ 1. Check pending registration
|
|
588
|
+
│ 2. Verify credentials
|
|
589
|
+
│ 3. Check email verified (if email enabled)
|
|
590
|
+
│ 4. Generate JWT tokens
|
|
512
591
|
▼
|
|
513
592
|
{ accessToken, refreshToken, user }
|
|
514
593
|
```
|
|
@@ -537,56 +616,148 @@ POST /auth/login { email, password }
|
|
|
537
616
|
POST /auth/login { email, password }
|
|
538
617
|
│
|
|
539
618
|
▼
|
|
540
|
-
┌─────────────────────────┐
|
|
541
|
-
│ AuthenticationService │
|
|
542
|
-
│ .login() │
|
|
543
|
-
└────────────┬────────────┘
|
|
544
|
-
│
|
|
545
|
-
│ Found: Multiple companies/branches
|
|
546
|
-
▼
|
|
547
619
|
{ requiresSelection: true, sessionId, companies: [...] }
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
620
|
+
│
|
|
621
|
+
│ User selects company & branch
|
|
622
|
+
▼
|
|
551
623
|
POST /auth/select { sessionId, companyId, branchId }
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
┌─────────────────────────┐
|
|
555
|
-
│ AuthenticationService │
|
|
556
|
-
│ .select() │
|
|
557
|
-
└────────────┬────────────┘
|
|
558
|
-
│
|
|
559
|
-
│ 1. Validate access via UserCompanyPermission
|
|
560
|
-
│ 2. Generate token with company context
|
|
561
|
-
▼
|
|
624
|
+
│
|
|
625
|
+
▼
|
|
562
626
|
{ accessToken, refreshToken, user, company, branch }
|
|
563
627
|
```
|
|
564
628
|
|
|
629
|
+
### Login with Email Verification Required
|
|
630
|
+
|
|
631
|
+
```
|
|
632
|
+
POST /auth/login { email, password }
|
|
633
|
+
│
|
|
634
|
+
▼
|
|
635
|
+
If pending registration OR email not verified:
|
|
636
|
+
{ requiresEmailVerification: true, email, message }
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
## Email Verification Flow
|
|
640
|
+
|
|
641
|
+
### New User Registration (with email enabled)
|
|
642
|
+
|
|
643
|
+
```
|
|
644
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
645
|
+
│ POST /auth/register { email, password, name } │
|
|
646
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
647
|
+
│
|
|
648
|
+
▼
|
|
649
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
650
|
+
│ 1. Check existing user (must not exist) │
|
|
651
|
+
│ 2. Hash password with bcrypt (SALT_ROUNDS = 12) │
|
|
652
|
+
│ 3. Generate verification token │
|
|
653
|
+
│ 4. Hash token with SHA-256 for storage │
|
|
654
|
+
│ 5. Create PendingRegistration record │
|
|
655
|
+
│ 6. Send verification email via AUTH_EMAIL_PROVIDER │
|
|
656
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
657
|
+
│
|
|
658
|
+
▼
|
|
659
|
+
{ requiresEmailVerification: true, message: "Check your email" }
|
|
660
|
+
│
|
|
661
|
+
│ User clicks email link
|
|
662
|
+
▼
|
|
663
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
664
|
+
│ POST /auth/verify-email { token } │
|
|
665
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
666
|
+
│
|
|
667
|
+
▼
|
|
668
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
669
|
+
│ 1. Hash incoming token with SHA-256 │
|
|
670
|
+
│ 2. Find PendingRegistration by hashed token │
|
|
671
|
+
│ 3. Check expiration │
|
|
672
|
+
│ 4. Delete PendingRegistration record │
|
|
673
|
+
│ 5. Create User with emailVerified: true │
|
|
674
|
+
│ 6. Process company assignment if companyData present │
|
|
675
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
676
|
+
│
|
|
677
|
+
▼
|
|
678
|
+
{ accessToken, refreshToken, user, company?, branch? }
|
|
679
|
+
```
|
|
680
|
+
|
|
681
|
+
### Password Reset Flow
|
|
682
|
+
|
|
683
|
+
```
|
|
684
|
+
POST /auth/forgot-password { email }
|
|
685
|
+
│
|
|
686
|
+
▼
|
|
687
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
688
|
+
│ 1. Find user by email │
|
|
689
|
+
│ 2. Delete existing PASSWORD_RESET tokens for user │
|
|
690
|
+
│ 3. Generate new token, hash with SHA-256 │
|
|
691
|
+
│ 4. Create UserToken record (expires in 1 hour) │
|
|
692
|
+
│ 5. Send reset email via AUTH_EMAIL_PROVIDER │
|
|
693
|
+
│ 6. Return success (same response even if user not found) │
|
|
694
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
695
|
+
│
|
|
696
|
+
▼
|
|
697
|
+
{ success: true, message: "If email exists, reset link sent" }
|
|
698
|
+
│
|
|
699
|
+
│ User clicks email link
|
|
700
|
+
▼
|
|
701
|
+
POST /auth/reset-password { token, newPassword }
|
|
702
|
+
│
|
|
703
|
+
▼
|
|
704
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
705
|
+
│ 1. Hash incoming token with SHA-256 │
|
|
706
|
+
│ 2. Find UserToken by hashed token + type: PASSWORD_RESET │
|
|
707
|
+
│ 3. Validate not expired, not used │
|
|
708
|
+
│ 4. Update user password │
|
|
709
|
+
│ 5. Mark token as used (usedAt = now) │
|
|
710
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
711
|
+
│
|
|
712
|
+
▼
|
|
713
|
+
{ success: true, message: "Password reset successfully" }
|
|
714
|
+
```
|
|
715
|
+
|
|
565
716
|
## User Management
|
|
566
717
|
|
|
567
|
-
###
|
|
718
|
+
### Company Permission Assignment Flow
|
|
719
|
+
|
|
720
|
+
When creating users with `enableCompanyFeature: true`, permissions are automatically assigned:
|
|
721
|
+
|
|
722
|
+
```
|
|
723
|
+
User Creation (with companyId/branchId)
|
|
724
|
+
│
|
|
725
|
+
▼
|
|
726
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
727
|
+
│ 1. Insert user record │
|
|
728
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
729
|
+
│
|
|
730
|
+
▼
|
|
731
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
732
|
+
│ 2. Check if company permission exists │
|
|
733
|
+
│ - Lookup UserCompanyPermission for user + company │
|
|
734
|
+
│ - If not exists: create { userId, targetId: companyId, │
|
|
735
|
+
│ permissionType: 'company' } │
|
|
736
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
737
|
+
│
|
|
738
|
+
▼
|
|
739
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
740
|
+
│ 3. Check if branch permission exists │
|
|
741
|
+
│ - Lookup UserCompanyPermission for user + branch │
|
|
742
|
+
│ - If not exists: create { userId, targetId: branchId, │
|
|
743
|
+
│ permissionType: 'branch' } │
|
|
744
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
745
|
+
```
|
|
568
746
|
|
|
569
|
-
|
|
747
|
+
### User Active Status
|
|
570
748
|
|
|
571
749
|
**When company feature is disabled:**
|
|
572
750
|
- Updates `User.isActive` directly
|
|
573
|
-
- Simple on/off for the user globally
|
|
574
751
|
|
|
575
752
|
**When company feature is enabled:**
|
|
576
753
|
- Updates `UserCompanyPermission.isActive` for the logged user's company
|
|
577
754
|
- User can be active in Company A but inactive in Company B
|
|
578
|
-
- `getAll` users filters by company permissions
|
|
579
|
-
- `isActive` in responses reflects company permission status
|
|
580
755
|
|
|
581
|
-
### Registration
|
|
756
|
+
### Registration Flows
|
|
582
757
|
|
|
583
758
|
**Simple registration:**
|
|
584
759
|
```json
|
|
585
|
-
{
|
|
586
|
-
"name": "John Doe",
|
|
587
|
-
"email": "john@example.com",
|
|
588
|
-
"password": "password123"
|
|
589
|
-
}
|
|
760
|
+
{ "name": "John Doe", "email": "john@example.com", "password": "password123" }
|
|
590
761
|
```
|
|
591
762
|
|
|
592
763
|
**Join existing company:**
|
|
@@ -607,160 +778,154 @@ The `PUT /users/:id/status` endpoint handles user active status based on company
|
|
|
607
778
|
"email": "john@example.com",
|
|
608
779
|
"password": "password123",
|
|
609
780
|
"newCompanyName": "My Company",
|
|
610
|
-
"
|
|
781
|
+
"newCompanyPhone": "+1234567890",
|
|
782
|
+
"newCompanyAddress": "123 Main St"
|
|
611
783
|
}
|
|
612
784
|
```
|
|
613
785
|
|
|
614
|
-
|
|
786
|
+
## No-IAM Mode
|
|
615
787
|
|
|
616
|
-
|
|
617
|
-
Company: "Acme Corporation"
|
|
618
|
-
│
|
|
619
|
-
├── Main Office (Branch)
|
|
620
|
-
│ ├── Sales Department (Sub-Branch)
|
|
621
|
-
│ │ ├── Inside Sales (Sub-Sub-Branch)
|
|
622
|
-
│ │ └── Outside Sales
|
|
623
|
-
│ └── Support Department
|
|
624
|
-
│ ├── Tier 1 Support
|
|
625
|
-
│ └── Tier 2 Support
|
|
626
|
-
│
|
|
627
|
-
└── Regional Office (Branch)
|
|
628
|
-
├── East Branch
|
|
629
|
-
└── West Branch
|
|
630
|
-
```
|
|
631
|
-
|
|
632
|
-
## Environment Variables
|
|
788
|
+
When not using `@flusys/nestjs-iam` but controllers use `level: 'permission'` security, set wildcard permissions after login:
|
|
633
789
|
|
|
634
|
-
```
|
|
635
|
-
|
|
636
|
-
DB_HOST=localhost
|
|
637
|
-
DB_PORT=3306
|
|
638
|
-
DB_USERNAME=root
|
|
639
|
-
DB_PASSWORD=password
|
|
640
|
-
DB_DATABASE=myapp
|
|
790
|
+
```typescript
|
|
791
|
+
import { HybridCache, CACHE_INSTANCE, PERMISSIONS_CACHE_PREFIX } from '@flusys/nestjs-shared';
|
|
641
792
|
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
REFRESH_TOKEN_COOKIE_NAME=fsn_refresh_token
|
|
648
|
-
```
|
|
793
|
+
@Injectable()
|
|
794
|
+
export class AuthenticationService {
|
|
795
|
+
constructor(
|
|
796
|
+
@Inject(CACHE_INSTANCE) private readonly cache: HybridCache,
|
|
797
|
+
) {}
|
|
649
798
|
|
|
650
|
-
|
|
799
|
+
async login(dto: LoginDto): Promise<LoginResponse> {
|
|
800
|
+
// ... validate credentials, generate tokens
|
|
651
801
|
|
|
652
|
-
|
|
802
|
+
// When IAM is NOT used, grant all permissions via wildcard
|
|
803
|
+
await this.setWildcardPermissions(user.id);
|
|
653
804
|
|
|
654
|
-
|
|
805
|
+
return { accessToken, user };
|
|
806
|
+
}
|
|
655
807
|
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
808
|
+
private async setWildcardPermissions(userId: string): Promise<void> {
|
|
809
|
+
const cacheKey = `${PERMISSIONS_CACHE_PREFIX}:user:${userId}`;
|
|
810
|
+
await this.cache.set(cacheKey, ['*'], 86400000); // 24 hours TTL
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
```
|
|
659
814
|
|
|
660
|
-
|
|
661
|
-
npm run migration:run
|
|
815
|
+
### With Company Feature Enabled
|
|
662
816
|
|
|
663
|
-
|
|
664
|
-
|
|
817
|
+
```typescript
|
|
818
|
+
private async setWildcardPermissions(
|
|
819
|
+
userId: string,
|
|
820
|
+
companyId?: string,
|
|
821
|
+
branchId?: string,
|
|
822
|
+
): Promise<void> {
|
|
823
|
+
if (companyId) {
|
|
824
|
+
const cacheKey = `${PERMISSIONS_CACHE_PREFIX}:company:${companyId}:branch:${branchId || 'null'}:user:${userId}`;
|
|
825
|
+
await this.cache.set(cacheKey, ['*'], 86400000);
|
|
826
|
+
} else {
|
|
827
|
+
const cacheKey = `${PERMISSIONS_CACHE_PREFIX}:user:${userId}`;
|
|
828
|
+
await this.cache.set(cacheKey, ['*'], 86400000);
|
|
829
|
+
}
|
|
830
|
+
}
|
|
665
831
|
```
|
|
666
832
|
|
|
667
|
-
|
|
833
|
+
### Wildcard Permission Patterns
|
|
668
834
|
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
enableCompanyFeature: false,
|
|
675
|
-
},
|
|
676
|
-
})
|
|
835
|
+
| Pattern | Meaning |
|
|
836
|
+
|---------|---------|
|
|
837
|
+
| `*` | Full access to everything |
|
|
838
|
+
| `user.*` | All user permissions |
|
|
839
|
+
| `branch.*` | All branch permissions |
|
|
677
840
|
|
|
678
|
-
|
|
679
|
-
Tables: 1
|
|
680
|
-
Features: Basic login, registration
|
|
681
|
-
Size: Minimal
|
|
682
|
-
Performance: Fast
|
|
683
|
-
```
|
|
841
|
+
## API Reference
|
|
684
842
|
|
|
685
|
-
###
|
|
843
|
+
### Exports
|
|
686
844
|
|
|
687
845
|
```typescript
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
bootstrapAppConfig: {
|
|
691
|
-
enableCompanyFeature: true,
|
|
692
|
-
},
|
|
693
|
-
})
|
|
846
|
+
// Config
|
|
847
|
+
export { AUTH_MODULE_OPTIONS, BCRYPT_SALT_ROUNDS, DEFAULT_COOKIE_NAME, MAX_COOKIE_BYTES, SESSION_CACHE_PREFIX, TOKEN_REVOKED_PREFIX } from './config';
|
|
694
848
|
|
|
695
|
-
Entities
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
849
|
+
// Entities
|
|
850
|
+
export { User } from './entities/user.entity';
|
|
851
|
+
export { UserToken, TokenType } from './entities/user-token.entity';
|
|
852
|
+
export { PendingRegistration } from './entities/pending-registration.entity';
|
|
853
|
+
export { Company } from './entities/company.entity';
|
|
854
|
+
export { CompanyBranch } from './entities/company-branch.entity';
|
|
855
|
+
export { UserCompanyPermission, PermissionType } from './entities/user-company-permission.entity';
|
|
856
|
+
export { getEntitiesByConfig, IAuthEntitiesConfig } from './entities';
|
|
702
857
|
|
|
703
|
-
|
|
858
|
+
// Enums
|
|
859
|
+
export { PermissionType } from './enums';
|
|
704
860
|
|
|
705
|
-
|
|
861
|
+
// Interfaces
|
|
862
|
+
export { IAuthEmailProvider, AUTH_EMAIL_PROVIDER } from './interfaces/auth-email-provider.interface';
|
|
863
|
+
export { IUserEnricher, IProfileSection, IProfileExtras, IEnrichedUser } from './interfaces/user-enricher.interface';
|
|
864
|
+
export { AuthModuleOptions, AuthModuleAsyncOptions, AuthOptionsFactory } from './interfaces/auth-module-options.interface';
|
|
865
|
+
export { TokenPayload, TokenPayloadWithCompany, LoginResponse, MeResponse, RegistrationResponse } from './interfaces/authentication.interface';
|
|
706
866
|
|
|
707
|
-
|
|
867
|
+
// Services
|
|
868
|
+
export { AuthenticationService } from './services/authentication.service';
|
|
869
|
+
export { AuthEmailService } from './services/auth-email.service';
|
|
870
|
+
export { AuthConfigService } from './services/auth-config.service';
|
|
871
|
+
export { AuthDataSourceProvider } from './services/auth-datasource.provider';
|
|
872
|
+
export { UserService } from './services/user.service';
|
|
873
|
+
export { CompanyService } from './services/company.service';
|
|
874
|
+
export { BranchService } from './services/branch.service';
|
|
875
|
+
export { UserPermissionService } from './services/user-permission.service';
|
|
876
|
+
export { CompanySelectionSessionService } from './services/company-selection-session.service';
|
|
708
877
|
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
// Auth module calls TypeOrmModule.forRoot() with auth entities
|
|
712
|
-
TypeOrmModule.forRoot({ entities: [User, Company, ...] })
|
|
878
|
+
// Modules
|
|
879
|
+
export { AuthModule } from './modules/auth.module';
|
|
713
880
|
|
|
714
|
-
//
|
|
715
|
-
|
|
716
|
-
```
|
|
881
|
+
// Strategies
|
|
882
|
+
export { JwtStrategy } from './strategies/jwt.strategy';
|
|
717
883
|
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
AuthModule.forRoot({
|
|
722
|
-
includeIAM: true, // Auth module loads IAM entities
|
|
723
|
-
// ...
|
|
724
|
-
})
|
|
884
|
+
// Interceptors
|
|
885
|
+
export { SetToken } from './interceptors/set-token.interceptor';
|
|
886
|
+
export { ClearToken } from './interceptors/clear-token.interceptor';
|
|
725
887
|
|
|
726
|
-
//
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
888
|
+
// DTOs
|
|
889
|
+
export * from './dtos';
|
|
890
|
+
|
|
891
|
+
// Swagger Config
|
|
892
|
+
export { authSwaggerConfig } from './docs/auth-swagger.config';
|
|
730
893
|
```
|
|
731
894
|
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
895
|
+
### Module Static Methods
|
|
896
|
+
|
|
897
|
+
```typescript
|
|
898
|
+
// Get entities based on feature flags
|
|
899
|
+
// Supports both boolean (legacy) and config object signatures
|
|
900
|
+
AuthModule.getEntities(config?: boolean | IBootstrapAppConfig): any[]
|
|
736
901
|
|
|
737
|
-
|
|
902
|
+
// Examples:
|
|
903
|
+
AuthModule.getEntities() // BaseEntities + EmailEntities
|
|
904
|
+
AuthModule.getEntities(true) // All entities (backward compat)
|
|
905
|
+
AuthModule.getEntities(false) // BaseEntities + EmailEntities
|
|
906
|
+
AuthModule.getEntities(bootstrapAppConfig) // Based on config flags
|
|
907
|
+
AuthModule.getEntities({ enableCompanyFeature: true, enableEmailVerification: false })
|
|
738
908
|
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
│ └── TypeOrmModule.forRoot([Auth entities + IAM entities])
|
|
743
|
-
│
|
|
744
|
-
└── IAMModule.forRoot()
|
|
745
|
-
└── TypeOrmModule.forFeature([IAM entities])
|
|
909
|
+
// Configure module
|
|
910
|
+
AuthModule.forRoot(options: AuthModuleOptions): DynamicModule
|
|
911
|
+
AuthModule.forRootAsync(options: AuthModuleAsyncOptions): DynamicModule
|
|
746
912
|
```
|
|
747
913
|
|
|
748
|
-
|
|
914
|
+
### Swagger Schema Behavior
|
|
749
915
|
|
|
750
|
-
|
|
916
|
+
When `enableCompanyFeature: false`, the following are automatically hidden from Swagger:
|
|
751
917
|
|
|
752
|
-
|
|
918
|
+
**Excluded Tags:** Companies, Branches, User Permissions, Company Selection
|
|
753
919
|
|
|
754
|
-
|
|
920
|
+
**Excluded DTO Properties:**
|
|
755
921
|
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
- **Performance** - Only load what you need
|
|
922
|
+
| DTO | Hidden Fields |
|
|
923
|
+
|-----|---------------|
|
|
924
|
+
| `RegistrationDto` | `companySlug`, `branchSlug`, `newCompanyName`, `newCompanyPhone`, `newCompanyAddress` |
|
|
925
|
+
| `RegistrationResponseDto` | `company`, `branch`, `companyFeatureEnabled` |
|
|
926
|
+
| `LoginResponseDto` | `company`, `branch`, `companyFeatureEnabled` |
|
|
927
|
+
| `MeResponseDto` | `company`, `branch` |
|
|
763
928
|
|
|
764
929
|
---
|
|
765
930
|
|
|
766
|
-
**Last Updated:** 2026-02-
|
|
931
|
+
**Last Updated:** 2026-02-22
|