@flusys/ng-auth 0.1.0-beta.1 → 0.1.0-beta.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.
Files changed (2) hide show
  1. package/README.md +775 -0
  2. package/package.json +4 -4
package/README.md ADDED
@@ -0,0 +1,775 @@
1
+ # @flusys/ng-auth Package Guide
2
+
3
+ ## Overview
4
+
5
+ `@flusys/ng-auth` provides complete authentication functionality including login, registration, token management, company/branch selection, user administration, and permission handling.
6
+
7
+ **Package:** `@flusys/ng-auth`
8
+ **Dependencies:** ng-core, ng-shared, ng-layout
9
+ **Build:** `npm run build:ng-auth`
10
+
11
+ ## Architecture
12
+
13
+ ```
14
+ ┌─────────────────────────────────────────────────────────────┐
15
+ │ ng-auth │
16
+ ├─────────────────────────────────────────────────────────────┤
17
+ │ Services │
18
+ │ ├── AuthApiService (login, register, refresh, logout) │
19
+ │ ├── AuthStateService (signal-based state management) │
20
+ │ ├── AuthInitService (session restoration on refresh) │
21
+ │ ├── TokenRefreshStateService (concurrent refresh handling) │
22
+ │ ├── UserApiService (user CRUD) │
23
+ │ ├── CompanyApiService (company CRUD) │
24
+ │ ├── BranchApiService (branch CRUD) │
25
+ │ └── UserPermissionApiService (permission assignments) │
26
+ ├─────────────────────────────────────────────────────────────┤
27
+ │ Guards │
28
+ │ ├── authGuard (requires authentication) │
29
+ │ ├── guestGuard (allows only unauthenticated) │
30
+ │ └── companyFeatureGuard (requires company feature enabled) │
31
+ ├─────────────────────────────────────────────────────────────┤
32
+ │ Interceptors │
33
+ │ ├── authInterceptor (adds JWT + tenant headers) │
34
+ │ └── tokenRefreshInterceptor (handles 401 + token refresh) │
35
+ ├─────────────────────────────────────────────────────────────┤
36
+ │ Adapters (Provider Interface Pattern) │
37
+ │ ├── AuthLayoutStateAdapter → LAYOUT_AUTH_STATE │
38
+ │ ├── AuthLayoutApiAdapter → LAYOUT_AUTH_API │
39
+ │ ├── AuthUserProviderAdapter → USER_PROVIDER │
40
+ │ ├── AuthCompanyApiProviderAdapter → COMPANY_API_PROVIDER │
41
+ │ └── AuthUserPermissionProviderAdapter → USER_PERMISSION_PROVIDER │
42
+ ├─────────────────────────────────────────────────────────────┤
43
+ │ Constants │
44
+ │ └── AUTH_ENDPOINTS (endpoint path definitions) │
45
+ └─────────────────────────────────────────────────────────────┘
46
+ ```
47
+
48
+ ---
49
+
50
+ ## Services
51
+
52
+ ### AuthApiService
53
+
54
+ HTTP client for authentication endpoints.
55
+
56
+ ```typescript
57
+ import { AuthApiService } from '@flusys/ng-auth';
58
+
59
+ const authApi = inject(AuthApiService);
60
+
61
+ // Authentication
62
+ authApi.login({ email, password }); // POST /auth/login
63
+ authApi.register({ name, email, password }); // POST /auth/register
64
+ authApi.refresh(); // POST /auth/refresh
65
+ authApi.logout(); // POST /auth/logout
66
+ authApi.me(); // GET /auth/me
67
+
68
+ // Company/Branch selection
69
+ authApi.select({ companyId, branchId, sessionId }); // POST /auth/select
70
+ authApi.switchCompany({ companyId, branchId }); // POST /auth/switch-company
71
+
72
+ // Password management
73
+ authApi.changePassword({ currentPassword, newPassword });
74
+ authApi.forgotPassword({ email });
75
+ authApi.resetPassword({ token, newPassword });
76
+ ```
77
+
78
+ ### AuthStateService
79
+
80
+ Signal-based centralized auth state management with auto-persistence.
81
+
82
+ ```typescript
83
+ import { AuthStateService } from '@flusys/ng-auth';
84
+
85
+ const authState = inject(AuthStateService);
86
+
87
+ // Read-only signals
88
+ authState.user(); // Signal<IUserInfo | null>
89
+ authState.accessToken(); // Signal<string | null>
90
+ authState.company(); // Signal<ICompanyInfo | null>
91
+ authState.branch(); // Signal<IBranchInfo | null>
92
+ authState.availableCompanies(); // Signal<ICompanyWithBranches[]>
93
+ authState.selectionSessionId(); // Signal<string | null>
94
+
95
+ // Computed signals
96
+ authState.isAuthenticated(); // Signal<boolean>
97
+ authState.requiresCompanySelection(); // Signal<boolean>
98
+ authState.userName(); // Signal<string>
99
+ authState.companyName(); // Signal<string>
100
+ authState.branchName(); // Signal<string>
101
+ authState.tenantId(); // Signal<string | null>
102
+
103
+ // State mutations
104
+ authState.setLoginState(response); // After successful login
105
+ authState.setSelectionState(response); // After company/branch selection
106
+ authState.clearSelectionState(); // Clear pending selection
107
+ authState.resetState(); // Full logout
108
+ authState.navigateToLogin(returnUrl?);
109
+ authState.navigateAfterLogin();
110
+ ```
111
+
112
+ **Auto-persistence:** Tokens and company/branch IDs are automatically saved to localStorage via effects.
113
+
114
+ ### AuthInitService
115
+
116
+ Handles session restoration on page refresh.
117
+
118
+ ```typescript
119
+ import { AuthInitService } from '@flusys/ng-auth';
120
+
121
+ const authInit = inject(AuthInitService);
122
+
123
+ // Signals
124
+ authInit.initialized(); // Signal<boolean>
125
+ authInit.loading(); // Signal<boolean>
126
+ authInit.error(); // Signal<string | null>
127
+
128
+ // Restore session (called by appInitGuard)
129
+ authInit.initialize().subscribe({
130
+ next: (user) => console.log('Session restored:', user),
131
+ error: (err) => console.log('No session or expired')
132
+ });
133
+ ```
134
+
135
+ **Flow:**
136
+ 1. Attempts `POST /auth/refresh` with httpOnly cookie
137
+ 2. On success, calls `GET /auth/me` to get user info
138
+ 3. Updates AuthStateService with restored session
139
+ 4. Caches result to prevent duplicate calls
140
+
141
+ ### Resource API Services
142
+
143
+ All extend `ApiResourceService` from ng-shared with signal-based state.
144
+
145
+ ```typescript
146
+ import { UserApiService, CompanyApiService, BranchApiService } from '@flusys/ng-auth';
147
+
148
+ // Common signals (from ApiResourceService)
149
+ service.data(); // Signal<T[]>
150
+ service.isLoading(); // Signal<boolean>
151
+ service.error(); // Signal<string | null>
152
+
153
+ // Common methods
154
+ await service.insertAsync(data); // Create
155
+ await service.updateAsync(data); // Update
156
+ await service.deleteAsync(id); // Delete
157
+ await service.fetchByIdAsync(id); // Get one
158
+ service.fetchList(search, options); // Get list with pagination
159
+ ```
160
+
161
+ **UserApiService** - Endpoint: `administration/users`
162
+ ```typescript
163
+ userApi.verifyEmail(userId);
164
+ userApi.verifyPhone(userId);
165
+ userApi.updateStatus(userId, status);
166
+ userApi.updateProfile(data);
167
+ ```
168
+
169
+ **CompanyApiService** - Endpoint: `administration/company`
170
+
171
+ **BranchApiService** - Endpoint: `administration/branch`
172
+ ```typescript
173
+ branchApi.currentCompanyId(); // Signal for company context
174
+ branchApi.companyBranches(); // Computed: branches filtered by company
175
+ branchApi.setCurrentCompanyId(id);
176
+ branchApi.insertBranch(data); // Auto-sets companyId
177
+ branchApi.fetchByCompany(companyId);
178
+ ```
179
+
180
+ ### UserPermissionApiService
181
+
182
+ Manages user-to-company and user-to-branch permission assignments.
183
+
184
+ ```typescript
185
+ import { UserPermissionApiService } from '@flusys/ng-auth';
186
+
187
+ const permApi = inject(UserPermissionApiService);
188
+
189
+ // Company permissions
190
+ permApi.assignUserCompanies({
191
+ userId: 'user-1',
192
+ items: [
193
+ { targetId: 'company-1', isAdd: true }, // Assign
194
+ { targetId: 'company-2', isAdd: false } // Revoke
195
+ ]
196
+ });
197
+ permApi.getUserCompanies(userId); // Get user's companies
198
+ permApi.assignUserToCompany(userId, companyId); // Single assign
199
+ permApi.revokeUserFromCompany(userId, companyId); // Single revoke
200
+
201
+ // Branch permissions
202
+ permApi.assignUserBranches({
203
+ userId: 'user-1',
204
+ items: [
205
+ { targetId: 'branch-1', isAdd: true }
206
+ ]
207
+ });
208
+ permApi.getUserBranches(userId); // Get user's branches
209
+ permApi.assignUserToBranch(userId, branchId);
210
+ permApi.revokeUserFromBranch(userId, branchId);
211
+ ```
212
+
213
+ **Backend Endpoints:**
214
+ - `POST /administration/permissions/user-company/assign`
215
+ - `GET /administration/permissions/user-company?userId=xxx`
216
+ - `POST /administration/permissions/user-branch/assign`
217
+ - `GET /administration/permissions/user-branch?userId=xxx`
218
+
219
+ ---
220
+
221
+ ## Guards
222
+
223
+ ### authGuard
224
+
225
+ Protects routes requiring authentication.
226
+
227
+ ```typescript
228
+ import { authGuard } from '@flusys/ng-auth';
229
+
230
+ {
231
+ path: 'dashboard',
232
+ canActivate: [authGuard],
233
+ loadComponent: () => import('./dashboard.component')
234
+ }
235
+ ```
236
+
237
+ **Checks:**
238
+ 1. User is authenticated
239
+ 2. If company feature enabled: company AND branch are selected
240
+ 3. Redirects to `/auth/login` with `returnUrl` if checks fail
241
+
242
+ ### guestGuard
243
+
244
+ Protects auth routes (login, register) from authenticated users.
245
+
246
+ ```typescript
247
+ import { guestGuard } from '@flusys/ng-auth';
248
+
249
+ {
250
+ path: 'auth/login',
251
+ canActivate: [guestGuard],
252
+ loadComponent: () => import('./login.component')
253
+ }
254
+ ```
255
+
256
+ **Behavior:**
257
+ 1. Restores session via `AuthInitService.initialize()` first
258
+ 2. If authenticated AND company selected: redirects to home or returnUrl
259
+ 3. If selection pending: allows access (login handles Step 2)
260
+ 4. If not authenticated: allows access
261
+
262
+ ### companyFeatureGuard
263
+
264
+ Enables/disables company management routes based on config.
265
+
266
+ ```typescript
267
+ import { companyFeatureGuard } from '@flusys/ng-auth';
268
+
269
+ {
270
+ path: 'administration/company',
271
+ canActivate: [authGuard, companyFeatureGuard],
272
+ loadComponent: () => import('./company-list.component')
273
+ }
274
+ ```
275
+
276
+ **Checks:** `services.auth.enabled` in APP_CONFIG. Redirects to `/` if disabled.
277
+
278
+ ---
279
+
280
+ ## Interceptors
281
+
282
+ ### authInterceptor
283
+
284
+ Adds authentication headers to HTTP requests.
285
+
286
+ ```typescript
287
+ import { authInterceptor } from '@flusys/ng-auth';
288
+
289
+ provideHttpClient(
290
+ withInterceptors([authInterceptor, ...])
291
+ )
292
+ ```
293
+
294
+ **Adds:**
295
+ - `Authorization: Bearer {token}` if token exists
296
+ - Tenant header if multi-tenant enabled
297
+ - `withCredentials: true` for auth endpoints (cookie support)
298
+
299
+ ### tokenRefreshInterceptor
300
+
301
+ Handles 401 responses and token refresh.
302
+
303
+ ```typescript
304
+ import { tokenRefreshInterceptor } from '@flusys/ng-auth';
305
+
306
+ provideHttpClient(
307
+ withInterceptors([authInterceptor, tokenRefreshInterceptor, ...])
308
+ )
309
+ ```
310
+
311
+ **Behavior:**
312
+ 1. Intercepts 401 Unauthorized responses
313
+ 2. Skips refresh for: `/auth/refresh`, `/auth/login`, `/auth/register`, `/auth/select`
314
+ 3. Uses `TokenRefreshStateService` to queue concurrent requests during refresh
315
+ 4. On success: updates token, retries original request
316
+ 5. On failure: logs out, shows error, redirects to login
317
+
318
+ ---
319
+
320
+ ## Constants
321
+
322
+ ### AUTH_ENDPOINTS
323
+
324
+ Centralized endpoint definitions used by interceptors.
325
+
326
+ ```typescript
327
+ import { AUTH_ENDPOINTS, isAuthEndpoint, isEndpoint } from '@flusys/ng-auth';
328
+
329
+ // Endpoint constants
330
+ AUTH_ENDPOINTS.LOGIN // '/auth/login'
331
+ AUTH_ENDPOINTS.LOGOUT // '/auth/logout'
332
+ AUTH_ENDPOINTS.REFRESH // '/auth/refresh'
333
+ AUTH_ENDPOINTS.REGISTER // '/auth/register'
334
+ AUTH_ENDPOINTS.SELECT // '/auth/select'
335
+ AUTH_ENDPOINTS.SWITCH_COMPANY // '/auth/switch-company'
336
+
337
+ // Helper functions
338
+ isAuthEndpoint(url); // Check if URL is any auth endpoint
339
+ isEndpoint(url, endpoint); // Check if URL matches specific endpoint
340
+ ```
341
+
342
+ ---
343
+
344
+ ## Provider Adapters
345
+
346
+ ng-auth implements provider interfaces from ng-shared, enabling ng-iam and ng-storage to access auth data without direct dependencies.
347
+
348
+ ### provideAuthProviders()
349
+
350
+ Registers provider adapters for ng-iam/ng-storage.
351
+
352
+ ```typescript
353
+ import { provideAuthProviders } from '@flusys/ng-auth';
354
+
355
+ // app.config.ts
356
+ providers: [
357
+ ...provideAuthProviders()
358
+ ]
359
+ ```
360
+
361
+ **Registers:**
362
+ - `USER_PROVIDER` → `AuthUserProviderAdapter` (user list for ng-iam)
363
+ - `COMPANY_API_PROVIDER` → `AuthCompanyApiProviderAdapter` (company list for ng-iam)
364
+ - `USER_PERMISSION_PROVIDER` → `AuthUserPermissionProviderAdapter` (permission queries)
365
+
366
+ ### provideAuthLayoutIntegration()
367
+
368
+ Bridges ng-auth to ng-layout components.
369
+
370
+ ```typescript
371
+ import { provideAuthLayoutIntegration } from '@flusys/ng-auth';
372
+
373
+ // app.config.ts
374
+ providers: [
375
+ ...provideAuthLayoutIntegration()
376
+ ]
377
+ ```
378
+
379
+ **Registers:**
380
+ - `LAYOUT_AUTH_STATE` → `AuthLayoutStateAdapter` (user/company/branch signals)
381
+ - `LAYOUT_AUTH_API` → `AuthLayoutApiAdapter` (logout, switch company, etc.)
382
+
383
+ **AuthLayoutApiAdapter Methods:**
384
+ ```typescript
385
+ logOut();
386
+ navigateLogin(withUrl?: boolean);
387
+ switchCompany(companyId, branchId); // Triggers page reload
388
+ getUserCompanies(); // User's permitted companies
389
+ getCompanyBranches(companyId); // User's permitted branches
390
+ ```
391
+
392
+ ---
393
+
394
+ ## Pages
395
+
396
+ ### Authentication Pages
397
+
398
+ **LoginPageComponent** (`/auth/login`)
399
+ - Multi-step login: Step 1 = credentials, Step 2 = company/branch selection (if needed)
400
+ - Visual step indicator
401
+ - Auto-selects if only one company/branch available
402
+ - Stores returnUrl for post-login redirect
403
+
404
+ **RegisterPageComponent** (`/auth/register`)
405
+ - User registration with optional company creation
406
+ - Email/password validation
407
+ - Terms acceptance
408
+
409
+ ### Administration Pages
410
+
411
+ **AdministrationPageComponent** (`/administration`)
412
+ - Tabbed interface: Users, Companies, Branches
413
+ - Company/Branch tabs require company feature enabled
414
+
415
+ **User Management:**
416
+ - `UserListComponent` - LazyLoad datatable with Edit, Delete, Permission actions
417
+ - `UserFormComponent` - Create/edit user form
418
+
419
+ **Company Management:**
420
+ - `CompanyListComponent` - Company CRUD
421
+ - `CompanyFormComponent` - Create/edit company
422
+
423
+ **Branch Management:**
424
+ - `BranchListComponent` - Branch CRUD filtered by company
425
+ - `BranchFormComponent` - Create/edit branch
426
+
427
+ ---
428
+
429
+ ## Dialog Components
430
+
431
+ ### UserCompanyPermissionDialogComponent
432
+
433
+ Manages user-to-company assignments with checkboxes.
434
+
435
+ ```typescript
436
+ import { UserCompanyPermissionDialogComponent } from '@flusys/ng-auth';
437
+
438
+ @Component({
439
+ imports: [UserCompanyPermissionDialogComponent],
440
+ template: `
441
+ <lib-user-company-permission-dialog
442
+ [visible]="showDialog()"
443
+ [user]="selectedUser()"
444
+ (closed)="showDialog.set(false)"
445
+ (permissionsChanged)="reloadData()" />
446
+ `
447
+ })
448
+ ```
449
+
450
+ **Features:**
451
+ - Shows all companies with checkboxes
452
+ - Pre-checks assigned companies
453
+ - Immediate save on toggle (no Save button)
454
+ - Toast notifications for success/error
455
+
456
+ ### UserBranchPermissionDialogComponent
457
+
458
+ Manages user-to-branch assignments with company filtering.
459
+
460
+ ```typescript
461
+ import { UserBranchPermissionDialogComponent } from '@flusys/ng-auth';
462
+
463
+ @Component({
464
+ imports: [UserBranchPermissionDialogComponent],
465
+ template: `
466
+ <lib-user-branch-permission-dialog
467
+ [visible]="showDialog()"
468
+ [user]="selectedUser()"
469
+ (closed)="showDialog.set(false)"
470
+ (permissionsChanged)="reloadData()" />
471
+ `
472
+ })
473
+ ```
474
+
475
+ **Features:**
476
+ - Shows only companies where user has company-level permissions
477
+ - User selects company → branches load
478
+ - Immediate save on toggle
479
+ - Scoped to user's permitted companies
480
+
481
+ **Workflow:**
482
+ 1. Dialog opens → Loads user's permitted companies
483
+ 2. User selects company → Loads branches for that company
484
+ 3. User toggles branch checkboxes → Immediately assigns/revokes
485
+
486
+ ---
487
+
488
+ ## Routes
489
+
490
+ ### AUTH_ROUTES
491
+
492
+ Public authentication routes.
493
+
494
+ ```typescript
495
+ import { AUTH_ROUTES } from '@flusys/ng-auth';
496
+
497
+ // Routes: /auth/login, /auth/register
498
+ { path: 'auth', children: AUTH_ROUTES }
499
+ ```
500
+
501
+ ### ADMINISTRATION_ROUTES
502
+
503
+ Protected administration routes.
504
+
505
+ ```typescript
506
+ import { ADMINISTRATION_ROUTES } from '@flusys/ng-auth';
507
+
508
+ // Routes: /administration/users/*, /administration/company/*, /administration/branch/*
509
+ {
510
+ path: 'administration',
511
+ canActivate: [authGuard],
512
+ children: ADMINISTRATION_ROUTES
513
+ }
514
+ ```
515
+
516
+ ---
517
+
518
+ ## Setup
519
+
520
+ ### app.config.ts
521
+
522
+ ```typescript
523
+ import { ApplicationConfig } from '@angular/core';
524
+ import { provideHttpClient, withFetch, withInterceptors } from '@angular/common/http';
525
+ import { APP_CONFIG, errorCatchingInterceptor } from '@flusys/ng-core';
526
+ import {
527
+ authInterceptor,
528
+ tokenRefreshInterceptor,
529
+ provideAuthLayoutIntegration,
530
+ provideAuthProviders
531
+ } from '@flusys/ng-auth';
532
+
533
+ export const appConfig: ApplicationConfig = {
534
+ providers: [
535
+ provideHttpClient(
536
+ withFetch(),
537
+ withInterceptors([
538
+ authInterceptor,
539
+ tokenRefreshInterceptor,
540
+ errorCatchingInterceptor
541
+ ])
542
+ ),
543
+ { provide: APP_CONFIG, useValue: DEFAULT_APP_CONFIG },
544
+ ...provideAuthLayoutIntegration(),
545
+ ...provideAuthProviders()
546
+ ]
547
+ };
548
+ ```
549
+
550
+ ### app.routes.ts
551
+
552
+ ```typescript
553
+ import { Routes } from '@angular/router';
554
+ import { authGuard, guestGuard, AUTH_ROUTES, ADMINISTRATION_ROUTES } from '@flusys/ng-auth';
555
+
556
+ export const routes: Routes = [
557
+ { path: 'auth', children: AUTH_ROUTES },
558
+ {
559
+ path: '',
560
+ canActivate: [authGuard],
561
+ children: [
562
+ { path: 'dashboard', loadComponent: () => import('./dashboard.component') },
563
+ { path: 'administration', children: ADMINISTRATION_ROUTES }
564
+ ]
565
+ }
566
+ ];
567
+ ```
568
+
569
+ ---
570
+
571
+ ## Interfaces
572
+
573
+ ### Request DTOs
574
+
575
+ ```typescript
576
+ interface ILoginRequest { email: string; password: string; }
577
+ interface IRegistrationRequest { name: string; email: string; password: string; phone?: string; }
578
+ interface ISelectRequest { companyId: string; branchId?: string; sessionId: string; }
579
+ interface ISwitchCompanyRequest { companyId: string; branchId: string; }
580
+ interface IChangePasswordRequest { currentPassword: string; newPassword: string; }
581
+ ```
582
+
583
+ ### Response DTOs
584
+
585
+ ```typescript
586
+ interface ILoginResponse {
587
+ accessToken: string;
588
+ user: IUserInfo;
589
+ requiresSelection?: boolean;
590
+ sessionId?: string;
591
+ companies?: ICompanyWithBranches[];
592
+ expiresAt?: number;
593
+ }
594
+
595
+ interface ICompanySelectionResponse {
596
+ accessToken: string;
597
+ user: IUserInfo;
598
+ company: ICompanyInfo;
599
+ branch: IBranchInfo;
600
+ }
601
+
602
+ interface IMeResponse extends IUserInfo {}
603
+ interface IRefreshTokenResponse { accessToken: string; }
604
+ ```
605
+
606
+ ### Entity DTOs
607
+
608
+ ```typescript
609
+ interface IUser {
610
+ id: string;
611
+ name: string;
612
+ email: string;
613
+ phone?: string;
614
+ profilePictureId?: string;
615
+ isActive: boolean;
616
+ isEmailVerified: boolean;
617
+ isPhoneVerified: boolean;
618
+ lastLoginAt?: Date;
619
+ createdAt: Date;
620
+ updatedAt: Date;
621
+ }
622
+
623
+ interface ICompany {
624
+ id: string;
625
+ name: string;
626
+ slug: string;
627
+ logoId?: string;
628
+ address?: string;
629
+ phone?: string;
630
+ email?: string;
631
+ website?: string;
632
+ isActive: boolean;
633
+ createdAt: Date;
634
+ updatedAt: Date;
635
+ }
636
+
637
+ interface IBranch {
638
+ id: string;
639
+ name: string;
640
+ slug: string;
641
+ companyId: string;
642
+ parentId?: string;
643
+ logoId?: string;
644
+ address?: string;
645
+ phone?: string;
646
+ email?: string;
647
+ isActive: boolean;
648
+ createdAt: Date;
649
+ updatedAt: Date;
650
+ }
651
+ ```
652
+
653
+ ### Permission DTOs
654
+
655
+ ```typescript
656
+ interface IPermissionItem { targetId: string; isAdd: boolean; }
657
+ interface IAssignUserCompanyRequest { userId: string; items: IPermissionItem[]; }
658
+ interface IAssignUserBranchRequest { userId: string; items: IPermissionItem[]; }
659
+ interface IBatchOperationResponse { success: boolean; message: string; }
660
+ interface IUserCompanyPermission { id: string; userId: string; companyId: string; companyName: string; companySlug: string; }
661
+ interface IUserBranchPermission { id: string; userId: string; branchId: string; branchName: string; branchSlug: string; companyId: string; }
662
+ ```
663
+
664
+ ---
665
+
666
+ ## Best Practices
667
+
668
+ ### Component Organization
669
+
670
+ ```
671
+ projects/ng-auth/
672
+ ├── components/ ← Shared dialogs
673
+ │ ├── user-company-permission-dialog.component.ts
674
+ │ └── user-branch-permission-dialog.component.ts
675
+ ├── pages/ ← Route-specific pages
676
+ │ ├── login/
677
+ │ ├── register/
678
+ │ └── users/
679
+ ├── services/
680
+ ├── guards/
681
+ ├── interceptors/
682
+ └── adapters/
683
+ ```
684
+
685
+ **Rule:** Dialog components go in `components/`, page components go in `pages/`.
686
+
687
+ ### Permission Filtering Pattern
688
+
689
+ When building permission UI, filter by user's permitted scope:
690
+
691
+ ```typescript
692
+ // ✅ CORRECT - Filter by user's permitted companies
693
+ const response = await firstValueFrom(permApi.getUserCompanies(userId));
694
+ const userCompanies = response.data;
695
+
696
+ // ❌ WRONG - Don't show all companies
697
+ const allCompanies = await companyApi.fetchList(); // Security risk
698
+ ```
699
+
700
+ ### Token Management
701
+
702
+ - Use httpOnly cookies for refresh tokens (backend)
703
+ - Access tokens stored in memory via AuthStateService
704
+ - TokenRefreshInterceptor handles automatic refresh
705
+ - Auto-persistence for company/branch selection
706
+
707
+ ### Multi-Company Flow
708
+
709
+ 1. Login → Check if `requiresSelection: true`
710
+ 2. If yes → Show Step 2 (company/branch selection)
711
+ 3. Auto-select if only one company/branch
712
+ 4. Save selection → Navigate to dashboard
713
+
714
+ ---
715
+
716
+ ## Common Issues
717
+
718
+ ### Guards Not Working
719
+
720
+ **Problem:** Routes accessible without authentication.
721
+
722
+ **Solution:** Ensure authGuard is on parent route and session is restored:
723
+ ```typescript
724
+ {
725
+ path: '',
726
+ canActivate: [authGuard], // Parent guard
727
+ children: [...]
728
+ }
729
+ ```
730
+
731
+ ### Token Not Sent
732
+
733
+ **Problem:** 401 on all requests.
734
+
735
+ **Solution:** Ensure interceptor order is correct:
736
+ ```typescript
737
+ withInterceptors([
738
+ authInterceptor, // First: adds token
739
+ tokenRefreshInterceptor, // Second: handles 401
740
+ errorCatchingInterceptor // Last: shows errors
741
+ ])
742
+ ```
743
+
744
+ ### Layout Not Showing User
745
+
746
+ **Problem:** User info missing from topbar.
747
+
748
+ **Solution:** Ensure layout integration is provided:
749
+ ```typescript
750
+ providers: [
751
+ ...provideAuthLayoutIntegration()
752
+ ]
753
+ ```
754
+
755
+ ### Company Switcher Shows All Companies
756
+
757
+ **Problem:** Switcher shows companies user doesn't have access to.
758
+
759
+ **Solution:** `AuthLayoutApiAdapter.getUserCompanies()` uses `UserPermissionApiService.getUserCompanies(userId)` internally - ensure user has company permissions assigned.
760
+
761
+ ---
762
+
763
+ ## See Also
764
+
765
+ - [CORE-GUIDE.md](./CORE-GUIDE.md) - APP_CONFIG configuration
766
+ - [SHARED-GUIDE.md](./SHARED-GUIDE.md) - Provider interfaces
767
+ - [LAYOUT-GUIDE.md](./LAYOUT-GUIDE.md) - Layout integration
768
+ - [IAM-GUIDE.md](./IAM-GUIDE.md) - Permission management
769
+ - [../FLUSYS_NEST/docs/AUTH-GUIDE.md](../../FLUSYS_NEST/docs/AUTH-GUIDE.md) - Backend auth module
770
+
771
+ ---
772
+
773
+ **Last Updated:** 2026-02-07
774
+ **Package Version:** 1.0.1
775
+ **Angular Version:** 21
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flusys/ng-auth",
3
- "version": "0.1.0-beta.1",
3
+ "version": "0.1.0-beta.3",
4
4
  "description": "Authentication module for FLUSYS Angular applications",
5
5
  "license": "MIT",
6
6
  "peerDependencies": {
@@ -8,9 +8,9 @@
8
8
  "@angular/core": "^21.0.0",
9
9
  "@angular/forms": "^21.0.0",
10
10
  "@angular/router": "^21.0.0",
11
- "@flusys/ng-core": "^0.1.0-beta.1",
12
- "@flusys/ng-layout": "^0.1.0-beta.1",
13
- "@flusys/ng-shared": "^0.1.0-beta.1",
11
+ "@flusys/ng-core": "^0.1.0-beta.3",
12
+ "@flusys/ng-layout": "^0.1.0-beta.3",
13
+ "@flusys/ng-shared": "^0.1.0-beta.3",
14
14
  "@primeuix/themes": "^1.0.0",
15
15
  "primeicons": "^7.0.0",
16
16
  "primeng": "^21.0.0"