@flusys/ng-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 CHANGED
@@ -5,6 +5,7 @@
5
5
  `@flusys/ng-auth` provides complete authentication functionality including login, registration, token management, company/branch selection, user administration, and permission handling.
6
6
 
7
7
  **Package:** `@flusys/ng-auth`
8
+ **Version:** 1.1.0
8
9
  **Dependencies:** ng-core, ng-shared, ng-layout
9
10
  **Build:** `npm run build:ng-auth`
10
11
 
@@ -27,7 +28,9 @@
27
28
  │ Guards │
28
29
  │ ├── authGuard (requires authentication) │
29
30
  │ ├── guestGuard (allows only unauthenticated) │
30
- │ └── companyFeatureGuard (requires company feature enabled) │
31
+ │ ├── companyFeatureGuard (requires company feature enabled) │
32
+ │ ├── emailFeatureGuard (requires email service enabled) │
33
+ │ └── signUpFeatureGuard (requires sign-up enabled) │
31
34
  ├─────────────────────────────────────────────────────────────┤
32
35
  │ Interceptors │
33
36
  │ ├── authInterceptor (adds JWT + tenant headers) │
@@ -273,7 +276,39 @@ import { companyFeatureGuard } from '@flusys/ng-auth';
273
276
  }
274
277
  ```
275
278
 
276
- **Checks:** `services.auth.enabled` in APP_CONFIG. Redirects to `/` if disabled.
279
+ **Checks:** `enableCompanyFeature` in APP_CONFIG. Redirects to `/` if disabled.
280
+
281
+ ### emailFeatureGuard
282
+
283
+ Protects email-related routes (forgot-password, reset-password, verify-email).
284
+
285
+ ```typescript
286
+ import { emailFeatureGuard } from '@flusys/ng-auth';
287
+
288
+ {
289
+ path: 'auth/forgot-password',
290
+ canActivate: [emailFeatureGuard, guestGuard],
291
+ loadComponent: () => import('./forgot-password.component')
292
+ }
293
+ ```
294
+
295
+ **Checks:** `services.email.enabled` in APP_CONFIG. Redirects to `/auth/login` if disabled.
296
+
297
+ ### signUpFeatureGuard
298
+
299
+ Protects registration route when sign-up is disabled.
300
+
301
+ ```typescript
302
+ import { signUpFeatureGuard } from '@flusys/ng-auth';
303
+
304
+ {
305
+ path: 'auth/register',
306
+ canActivate: [signUpFeatureGuard, guestGuard],
307
+ loadComponent: () => import('./register.component')
308
+ }
309
+ ```
310
+
311
+ **Checks:** `enableSignUp` in APP_CONFIG (defaults to `true`). Redirects to `/auth/login` if `false`.
277
312
 
278
313
  ---
279
314
 
@@ -324,7 +359,7 @@ provideHttpClient(
324
359
  Centralized endpoint definitions used by interceptors.
325
360
 
326
361
  ```typescript
327
- import { AUTH_ENDPOINTS, isAuthEndpoint, isEndpoint } from '@flusys/ng-auth';
362
+ import { AUTH_ENDPOINTS, isEndpoint } from '@flusys/ng-auth';
328
363
 
329
364
  // Endpoint constants
330
365
  AUTH_ENDPOINTS.LOGIN // '/auth/login'
@@ -334,8 +369,7 @@ AUTH_ENDPOINTS.REGISTER // '/auth/register'
334
369
  AUTH_ENDPOINTS.SELECT // '/auth/select'
335
370
  AUTH_ENDPOINTS.SWITCH_COMPANY // '/auth/switch-company'
336
371
 
337
- // Helper functions
338
- isAuthEndpoint(url); // Check if URL is any auth endpoint
372
+ // Helper function
339
373
  isEndpoint(url, endpoint); // Check if URL matches specific endpoint
340
374
  ```
341
375
 
@@ -393,27 +427,61 @@ getCompanyBranches(companyId); // User's permitted branches
393
427
 
394
428
  ## Pages
395
429
 
430
+ All page components follow Angular 21 best practices:
431
+ - `ChangeDetectionStrategy.OnPush` for optimal performance
432
+ - Signal Forms (`@angular/forms/signals`) with validation
433
+ - 100% responsive design (mobile-first with Tailwind)
434
+ - Dark mode compatible (PrimeNG + Tailwind CSS variables)
435
+ - `inject()` for dependency injection
436
+
396
437
  ### Authentication Pages
397
438
 
398
439
  **LoginPageComponent** (`/auth/login`)
399
440
  - Multi-step login: Step 1 = credentials, Step 2 = company/branch selection (if needed)
400
- - Visual step indicator
441
+ - Visual step indicator (when company feature enabled)
442
+ - Email verification step (when email not verified)
401
443
  - Auto-selects if only one company/branch available
402
444
  - Stores returnUrl for post-login redirect
445
+ - Responsive form layout with mobile padding
446
+ - **Conditional UI based on config:**
447
+ - "Forgot password?" link hidden when `services.email.enabled: false`
448
+ - "Sign Up" link hidden when `enableSignUp: false`
403
449
 
404
450
  **RegisterPageComponent** (`/auth/register`)
405
451
  - User registration with optional company creation
406
- - Email/password validation
407
- - Terms acceptance
452
+ - Two-column responsive grid layout
453
+ - Email/password validation with signal forms
454
+ - Company details section (when company feature enabled)
455
+ - **Default company/branch support:**
456
+ - When `defaultCompanySlug` is set: pre-fills company code field and makes it readonly
457
+ - When `defaultBranchSlug` is set: pre-fills branch code field and makes it readonly
458
+ - "Create new company" option hidden when defaults are configured
459
+
460
+ **ForgotPasswordPageComponent** (`/auth/forgot-password`)
461
+ - Email input with validation
462
+ - Success state showing email sent confirmation
463
+
464
+ **ResetPasswordPageComponent** (`/auth/reset-password`)
465
+ - Password reset with token validation
466
+ - Password confirmation matching
467
+ - Success/invalid token states
468
+
469
+ **VerifyEmailPageComponent** (`/auth/verify-email`)
470
+ - Token verification with SSR support (TransferState)
471
+ - Resend verification option
472
+ - Success/failure states
408
473
 
409
474
  ### Profile Page
410
475
 
411
476
  **ProfilePageComponent** (`/profile`)
477
+ - Page title with description
478
+ - Avatar section with initials fallback
412
479
  - Update name and phone
413
480
  - Upload profile picture (requires `PROFILE_UPLOAD_PROVIDER` from ng-storage)
414
- - Change password
481
+ - Change password section with strength indicator
415
482
  - View company/branch info (if company feature enabled)
416
483
  - View roles/actions (requires `PROFILE_PERMISSION_PROVIDER` from ng-iam)
484
+ - Responsive two-column layout on desktop
417
485
 
418
486
  **Optional Provider Dependencies:**
419
487
 
@@ -430,25 +498,42 @@ getCompanyBranches(companyId); // User's permitted branches
430
498
  ### Administration Pages
431
499
 
432
500
  **AdministrationPageComponent** (`/administration`)
433
- - Tabbed interface: Users, Companies, Branches
434
- - Company/Branch tabs require company feature enabled
501
+ - Page title "Administration" with dynamic subtitle
502
+ - Horizontal scrolling tabs (hidden scrollbar on mobile)
503
+ - Tabs: Users, Companies, Branches
504
+ - Company/Branch tabs only visible when company feature enabled
435
505
 
436
506
  **User Management:**
437
507
  - `UserListComponent` - LazyLoad datatable with Edit, Delete, Permission actions
438
- - `UserFormComponent` - Create/edit user form
508
+ - `UserFormComponent` - Create/edit user with auto company/branch assignment
509
+ - Permission dialogs for company/branch assignments
439
510
 
440
511
  **Company Management:**
441
- - `CompanyListComponent` - Company CRUD
442
- - `CompanyFormComponent` - Create/edit company
512
+ - `CompanyListComponent` - Company CRUD with View Branches action
513
+ - `CompanyFormComponent` - Create/edit company with auto slug generation
443
514
 
444
515
  **Branch Management:**
445
- - `BranchListComponent` - Branch CRUD filtered by company
446
- - `BranchFormComponent` - Create/edit branch
516
+ - `BranchListComponent` - Branch CRUD filtered by company dropdown
517
+ - `BranchFormComponent` - Create/edit branch with company selection
518
+
519
+ **Table Features (all list components):**
520
+ - Responsive header (stacked on mobile)
521
+ - Horizontal scroll wrapper (`overflow-x-auto`)
522
+ - Conditional pagination (`[paginator]="total() > 0"`)
523
+ - Striped rows (`p-datatable-striped`)
524
+ - Lazy loading for performance
447
525
 
448
526
  ---
449
527
 
450
528
  ## Dialog Components
451
529
 
530
+ Both dialog components use Angular 21 patterns:
531
+ - `ChangeDetectionStrategy.OnPush`
532
+ - `input.required<T>()` for required inputs
533
+ - `output<void>()` for events
534
+ - `effect()` for reactive data loading
535
+ - Responsive breakpoints (`{ '960px': '90vw', '640px': '95vw' }`)
536
+
452
537
  ### UserCompanyPermissionDialogComponent
453
538
 
454
539
  Manages user-to-company assignments with checkboxes.
@@ -468,11 +553,21 @@ import { UserCompanyPermissionDialogComponent } from '@flusys/ng-auth';
468
553
  })
469
554
  ```
470
555
 
556
+ **Inputs/Outputs:**
557
+ ```typescript
558
+ readonly visible = input.required<boolean>();
559
+ readonly user = input.required<IUser | null>();
560
+ readonly closed = output<void>();
561
+ readonly permissionsChanged = output<void>();
562
+ ```
563
+
471
564
  **Features:**
472
565
  - Shows all companies with checkboxes
473
566
  - Pre-checks assigned companies
474
567
  - Immediate save on toggle (no Save button)
475
568
  - Toast notifications for success/error
569
+ - Loading spinner during data fetch
570
+ - Disabled checkboxes while saving
476
571
 
477
572
  ### UserBranchPermissionDialogComponent
478
573
 
@@ -494,13 +589,14 @@ import { UserBranchPermissionDialogComponent } from '@flusys/ng-auth';
494
589
  ```
495
590
 
496
591
  **Features:**
497
- - Shows only companies where user has company-level permissions
498
- - User selects company → branches load
592
+ - Company dropdown shows only user's permitted companies
593
+ - Branch list loads on company selection
499
594
  - Immediate save on toggle
595
+ - Contextual empty messages (no permissions vs no branches)
500
596
  - Scoped to user's permitted companies
501
597
 
502
598
  **Workflow:**
503
- 1. Dialog opens → Loads user's permitted companies
599
+ 1. Dialog opens → Loads user's permitted companies via effect()
504
600
  2. User selects company → Loads branches for that company
505
601
  3. User toggles branch checkboxes → Immediately assigns/revokes
506
602
 
@@ -706,6 +802,67 @@ interface IUserBranchPermission { id: string; userId: string; branchId: string;
706
802
 
707
803
  ## Best Practices
708
804
 
805
+ ### Angular 21 Component Patterns
806
+
807
+ All components in ng-auth follow these patterns:
808
+
809
+ ```typescript
810
+ @Component({
811
+ selector: 'lib-example',
812
+ standalone: true,
813
+ changeDetection: ChangeDetectionStrategy.OnPush, // Always OnPush
814
+ imports: [AngularModule, PrimeModule, FormField],
815
+ template: `...`
816
+ })
817
+ export class ExampleComponent {
818
+ // Dependency injection via inject()
819
+ private readonly service = inject(MyService);
820
+
821
+ // Signal-based state
822
+ readonly isLoading = signal(false);
823
+ readonly data = signal<IData | null>(null);
824
+
825
+ // Computed signals
826
+ readonly isValid = computed(() => this.data() !== null);
827
+
828
+ // Signal Forms
829
+ readonly formModel = signal<IFormModel>({ name: '', email: '' });
830
+ readonly formTree = form(this.formModel, (f) => {
831
+ required(f.name, { message: 'Name is required' });
832
+ });
833
+
834
+ // Error handling with proper typing
835
+ async onSubmit(): Promise<void> {
836
+ try {
837
+ // ...
838
+ } catch (error: unknown) {
839
+ const err = error as { error?: { message?: string } };
840
+ // Handle error
841
+ }
842
+ }
843
+ }
844
+ ```
845
+
846
+ ### Responsive Design Patterns
847
+
848
+ ```html
849
+ <!-- Responsive container -->
850
+ <div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 p-4">
851
+
852
+ <!-- Two-column form grid -->
853
+ <form class="grid grid-cols-1 md:grid-cols-2 gap-4">
854
+
855
+ <!-- Right-aligned buttons -->
856
+ <div class="flex justify-end gap-4 md:col-span-2 pt-4">
857
+
858
+ <!-- Horizontally scrollable tabs -->
859
+ <div class="scrollbar-hide flex gap-1 overflow-x-auto flex-nowrap">
860
+
861
+ <!-- Responsive table wrapper -->
862
+ <div class="overflow-x-auto">
863
+ <p-table [scrollable]="true" [paginator]="total() > 0">
864
+ ```
865
+
709
866
  ### Component Organization
710
867
 
711
868
  ```
@@ -716,7 +873,14 @@ projects/ng-auth/
716
873
  ├── pages/ ← Route-specific pages
717
874
  │ ├── login/
718
875
  │ ├── register/
719
- │ └── users/
876
+ │ ├── forgot-password/
877
+ │ ├── reset-password/
878
+ │ ├── verify-email/
879
+ │ ├── profile/
880
+ │ ├── administration/
881
+ │ ├── users/
882
+ │ ├── company/
883
+ │ └── branch/
720
884
  ├── services/
721
885
  ├── guards/
722
886
  ├── interceptors/
@@ -745,6 +909,27 @@ const allCompanies = await companyApi.fetchList(); // Security risk
745
909
  - TokenRefreshInterceptor handles automatic refresh
746
910
  - Auto-persistence for company/branch selection
747
911
 
912
+ ### Type Safety
913
+
914
+ Avoid `any` types - use explicit interfaces:
915
+
916
+ ```typescript
917
+ // ✅ CORRECT - Explicit response type
918
+ .subscribe({
919
+ next: (result: { message?: string }) => {
920
+ this.messageService.add({
921
+ severity: 'success',
922
+ detail: result.message,
923
+ });
924
+ },
925
+ });
926
+
927
+ // ❌ WRONG - Using any
928
+ .subscribe({
929
+ next: (result: any) => { ... },
930
+ });
931
+ ```
932
+
748
933
  ### Multi-Company Flow
749
934
 
750
935
  1. Login → Check if `requiresSelection: true`
@@ -801,6 +986,143 @@ providers: [
801
986
 
802
987
  ---
803
988
 
989
+ ## No-IAM Mode (Bypassing Permission Guards)
990
+
991
+ When you don't use the `@flusys/ng-iam` module but your routes use `permissionGuard`, the guard will fail because no permissions are loaded.
992
+
993
+ **Solution:** Set wildcard permissions in `PermissionValidatorService` after login.
994
+
995
+ ### How Permission Guard Works
996
+
997
+ ```
998
+ Route with permissionGuard('user.view')
999
+ ↓
1000
+ permissionGuard() → inject(PermissionValidatorService)
1001
+ ↓
1002
+ permissionValidator.isPermissionsLoaded() → false
1003
+ ↓
1004
+ Redirect to '/' ❌
1005
+ ```
1006
+
1007
+ ### Fix: Set Wildcard Permissions After Login
1008
+
1009
+ #### Option 1: In Login Flow (Recommended)
1010
+
1011
+ Update your login handling to set permissions after successful authentication:
1012
+
1013
+ ```typescript
1014
+ // login.component.ts or auth.service.ts
1015
+ import { PermissionValidatorService } from '@flusys/ng-shared';
1016
+
1017
+ @Component({ ... })
1018
+ export class LoginComponent {
1019
+ private readonly permissionValidator = inject(PermissionValidatorService);
1020
+ private readonly authApi = inject(AuthApiService);
1021
+ private readonly authState = inject(AuthStateService);
1022
+
1023
+ async onLogin(): Promise<void> {
1024
+ const response = await firstValueFrom(this.authApi.login(credentials));
1025
+ this.authState.setLoginState(response);
1026
+
1027
+ // When IAM is NOT used, grant all permissions via wildcard
1028
+ this.permissionValidator.setPermissions(['*']);
1029
+
1030
+ // Navigate to dashboard
1031
+ this.router.navigate(['/dashboard']);
1032
+ }
1033
+ }
1034
+ ```
1035
+
1036
+ #### Option 2: In APP_INITIALIZER
1037
+
1038
+ Set permissions at app startup:
1039
+
1040
+ ```typescript
1041
+ // app.config.ts
1042
+ import { APP_INITIALIZER } from '@angular/core';
1043
+ import { PermissionValidatorService } from '@flusys/ng-shared';
1044
+
1045
+ export const appConfig: ApplicationConfig = {
1046
+ providers: [
1047
+ {
1048
+ provide: APP_INITIALIZER,
1049
+ useFactory: (permissionValidator: PermissionValidatorService) => {
1050
+ return () => {
1051
+ // Set wildcard permissions when IAM is disabled
1052
+ permissionValidator.setPermissions(['*']);
1053
+ };
1054
+ },
1055
+ deps: [PermissionValidatorService],
1056
+ multi: true,
1057
+ },
1058
+ // ... other providers
1059
+ ],
1060
+ };
1061
+ ```
1062
+
1063
+ #### Option 3: In AuthInitService (After Session Restore)
1064
+
1065
+ If you restore sessions on page refresh, set permissions there:
1066
+
1067
+ ```typescript
1068
+ // After successful /auth/me response
1069
+ authInit.initialize().subscribe({
1070
+ next: (user) => {
1071
+ // Session restored, set wildcard permissions
1072
+ permissionValidator.setPermissions(['*']);
1073
+ },
1074
+ });
1075
+ ```
1076
+
1077
+ ### Wildcard Permission Syntax
1078
+
1079
+ The `permissionGuard` utilities support these patterns:
1080
+
1081
+ | Pattern | Meaning |
1082
+ |---------|---------|
1083
+ | `*` | Full access to everything |
1084
+ | `user.*` | All user permissions |
1085
+ | `branch.*` | All branch permissions |
1086
+
1087
+ ### For Jest Tests
1088
+
1089
+ Mock `PermissionValidatorService` in your test setup:
1090
+
1091
+ ```typescript
1092
+ import { PermissionValidatorService } from '@flusys/ng-shared';
1093
+
1094
+ describe('MyComponent', () => {
1095
+ beforeEach(async () => {
1096
+ await TestBed.configureTestingModule({
1097
+ imports: [MyComponent],
1098
+ providers: [
1099
+ {
1100
+ provide: PermissionValidatorService,
1101
+ useValue: {
1102
+ permissions: () => ['*'],
1103
+ isPermissionsLoaded: () => true,
1104
+ hasPermission: () => true,
1105
+ setPermissions: jest.fn(),
1106
+ clearPermissions: jest.fn(),
1107
+ },
1108
+ },
1109
+ ],
1110
+ }).compileComponents();
1111
+ });
1112
+ });
1113
+ ```
1114
+
1115
+ ### For Template Permission Checks
1116
+
1117
+ If using `*hasPermission` directive, it will also respect wildcard:
1118
+
1119
+ ```html
1120
+ <!-- This will show when permissions include '*' -->
1121
+ <button *hasPermission="'user.delete'">Delete User</button>
1122
+ ```
1123
+
1124
+ ---
1125
+
804
1126
  ## See Also
805
1127
 
806
1128
  - [CORE-GUIDE.md](./CORE-GUIDE.md) - APP_CONFIG configuration
@@ -811,5 +1133,26 @@ providers: [
811
1133
 
812
1134
  ---
813
1135
 
814
- **Last Updated:** 2026-02-16
1136
+ **Last Updated:** 2026-02-23
1137
+ **Version:** 1.1.0
815
1138
  **Angular Version:** 21
1139
+
1140
+ ## Component Checklist
1141
+
1142
+ | Component | OnPush | Signal Forms | Responsive | Dark Mode |
1143
+ |-----------|--------|--------------|------------|-----------|
1144
+ | LoginPageComponent | ✅ | ✅ | ✅ | ✅ |
1145
+ | RegisterPageComponent | ✅ | ✅ | ✅ | ✅ |
1146
+ | ForgotPasswordPageComponent | ✅ | ✅ | ✅ | ✅ |
1147
+ | ResetPasswordPageComponent | ✅ | ✅ | ✅ | ✅ |
1148
+ | VerifyEmailPageComponent | ✅ | ✅ | ✅ | ✅ |
1149
+ | ProfilePageComponent | ✅ | ✅ | ✅ | ✅ |
1150
+ | AdministrationPageComponent | ✅ | N/A | ✅ | ✅ |
1151
+ | UserListComponent | ✅ | N/A | ✅ | ✅ |
1152
+ | UserFormComponent | ✅ | ✅ | ✅ | ✅ |
1153
+ | CompanyListComponent | ✅ | N/A | ✅ | ✅ |
1154
+ | CompanyFormComponent | ✅ | ✅ | ✅ | ✅ |
1155
+ | BranchListComponent | ✅ | N/A | ✅ | ✅ |
1156
+ | BranchFormComponent | ✅ | ✅ | ✅ | ✅ |
1157
+ | UserCompanyPermissionDialogComponent | ✅ | N/A | ✅ | ✅ |
1158
+ | UserBranchPermissionDialogComponent | ✅ | N/A | ✅ | ✅ |