@flusys/ng-auth 1.1.1-beta → 2.0.0-rc.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 +319 -19
- package/fesm2022/flusys-ng-auth.mjs +1125 -1668
- package/fesm2022/flusys-ng-auth.mjs.map +1 -1
- package/package.json +4 -4
- package/types/flusys-ng-auth.d.ts +165 -647
package/README.md
CHANGED
|
@@ -324,7 +324,7 @@ provideHttpClient(
|
|
|
324
324
|
Centralized endpoint definitions used by interceptors.
|
|
325
325
|
|
|
326
326
|
```typescript
|
|
327
|
-
import { AUTH_ENDPOINTS,
|
|
327
|
+
import { AUTH_ENDPOINTS, isEndpoint } from '@flusys/ng-auth';
|
|
328
328
|
|
|
329
329
|
// Endpoint constants
|
|
330
330
|
AUTH_ENDPOINTS.LOGIN // '/auth/login'
|
|
@@ -334,8 +334,7 @@ AUTH_ENDPOINTS.REGISTER // '/auth/register'
|
|
|
334
334
|
AUTH_ENDPOINTS.SELECT // '/auth/select'
|
|
335
335
|
AUTH_ENDPOINTS.SWITCH_COMPANY // '/auth/switch-company'
|
|
336
336
|
|
|
337
|
-
// Helper
|
|
338
|
-
isAuthEndpoint(url); // Check if URL is any auth endpoint
|
|
337
|
+
// Helper function
|
|
339
338
|
isEndpoint(url, endpoint); // Check if URL matches specific endpoint
|
|
340
339
|
```
|
|
341
340
|
|
|
@@ -393,27 +392,54 @@ getCompanyBranches(companyId); // User's permitted branches
|
|
|
393
392
|
|
|
394
393
|
## Pages
|
|
395
394
|
|
|
395
|
+
All page components follow Angular 21 best practices:
|
|
396
|
+
- `ChangeDetectionStrategy.OnPush` for optimal performance
|
|
397
|
+
- Signal Forms (`@angular/forms/signals`) with validation
|
|
398
|
+
- 100% responsive design (mobile-first with Tailwind)
|
|
399
|
+
- Dark mode compatible (PrimeNG + Tailwind CSS variables)
|
|
400
|
+
- `inject()` for dependency injection
|
|
401
|
+
|
|
396
402
|
### Authentication Pages
|
|
397
403
|
|
|
398
404
|
**LoginPageComponent** (`/auth/login`)
|
|
399
405
|
- Multi-step login: Step 1 = credentials, Step 2 = company/branch selection (if needed)
|
|
400
|
-
- Visual step indicator
|
|
406
|
+
- Visual step indicator (when company feature enabled)
|
|
407
|
+
- Email verification step (when email not verified)
|
|
401
408
|
- Auto-selects if only one company/branch available
|
|
402
409
|
- Stores returnUrl for post-login redirect
|
|
410
|
+
- Responsive form layout with mobile padding
|
|
403
411
|
|
|
404
412
|
**RegisterPageComponent** (`/auth/register`)
|
|
405
413
|
- User registration with optional company creation
|
|
406
|
-
-
|
|
407
|
-
-
|
|
414
|
+
- Two-column responsive grid layout
|
|
415
|
+
- Email/password validation with signal forms
|
|
416
|
+
- Company details section (when company feature enabled)
|
|
417
|
+
|
|
418
|
+
**ForgotPasswordPageComponent** (`/auth/forgot-password`)
|
|
419
|
+
- Email input with validation
|
|
420
|
+
- Success state showing email sent confirmation
|
|
421
|
+
|
|
422
|
+
**ResetPasswordPageComponent** (`/auth/reset-password`)
|
|
423
|
+
- Password reset with token validation
|
|
424
|
+
- Password confirmation matching
|
|
425
|
+
- Success/invalid token states
|
|
426
|
+
|
|
427
|
+
**VerifyEmailPageComponent** (`/auth/verify-email`)
|
|
428
|
+
- Token verification with SSR support (TransferState)
|
|
429
|
+
- Resend verification option
|
|
430
|
+
- Success/failure states
|
|
408
431
|
|
|
409
432
|
### Profile Page
|
|
410
433
|
|
|
411
434
|
**ProfilePageComponent** (`/profile`)
|
|
435
|
+
- Page title with description
|
|
436
|
+
- Avatar section with initials fallback
|
|
412
437
|
- Update name and phone
|
|
413
438
|
- Upload profile picture (requires `PROFILE_UPLOAD_PROVIDER` from ng-storage)
|
|
414
|
-
- Change password
|
|
439
|
+
- Change password section with strength indicator
|
|
415
440
|
- View company/branch info (if company feature enabled)
|
|
416
441
|
- View roles/actions (requires `PROFILE_PERMISSION_PROVIDER` from ng-iam)
|
|
442
|
+
- Responsive two-column layout on desktop
|
|
417
443
|
|
|
418
444
|
**Optional Provider Dependencies:**
|
|
419
445
|
|
|
@@ -430,25 +456,42 @@ getCompanyBranches(companyId); // User's permitted branches
|
|
|
430
456
|
### Administration Pages
|
|
431
457
|
|
|
432
458
|
**AdministrationPageComponent** (`/administration`)
|
|
433
|
-
-
|
|
434
|
-
-
|
|
459
|
+
- Page title "Administration" with dynamic subtitle
|
|
460
|
+
- Horizontal scrolling tabs (hidden scrollbar on mobile)
|
|
461
|
+
- Tabs: Users, Companies, Branches
|
|
462
|
+
- Company/Branch tabs only visible when company feature enabled
|
|
435
463
|
|
|
436
464
|
**User Management:**
|
|
437
465
|
- `UserListComponent` - LazyLoad datatable with Edit, Delete, Permission actions
|
|
438
|
-
- `UserFormComponent` - Create/edit user
|
|
466
|
+
- `UserFormComponent` - Create/edit user with auto company/branch assignment
|
|
467
|
+
- Permission dialogs for company/branch assignments
|
|
439
468
|
|
|
440
469
|
**Company Management:**
|
|
441
|
-
- `CompanyListComponent` - Company CRUD
|
|
442
|
-
- `CompanyFormComponent` - Create/edit company
|
|
470
|
+
- `CompanyListComponent` - Company CRUD with View Branches action
|
|
471
|
+
- `CompanyFormComponent` - Create/edit company with auto slug generation
|
|
443
472
|
|
|
444
473
|
**Branch Management:**
|
|
445
|
-
- `BranchListComponent` - Branch CRUD filtered by company
|
|
446
|
-
- `BranchFormComponent` - Create/edit branch
|
|
474
|
+
- `BranchListComponent` - Branch CRUD filtered by company dropdown
|
|
475
|
+
- `BranchFormComponent` - Create/edit branch with company selection
|
|
476
|
+
|
|
477
|
+
**Table Features (all list components):**
|
|
478
|
+
- Responsive header (stacked on mobile)
|
|
479
|
+
- Horizontal scroll wrapper (`overflow-x-auto`)
|
|
480
|
+
- Conditional pagination (`[paginator]="total() > 0"`)
|
|
481
|
+
- Striped rows (`p-datatable-striped`)
|
|
482
|
+
- Lazy loading for performance
|
|
447
483
|
|
|
448
484
|
---
|
|
449
485
|
|
|
450
486
|
## Dialog Components
|
|
451
487
|
|
|
488
|
+
Both dialog components use Angular 21 patterns:
|
|
489
|
+
- `ChangeDetectionStrategy.OnPush`
|
|
490
|
+
- `input.required<T>()` for required inputs
|
|
491
|
+
- `output<void>()` for events
|
|
492
|
+
- `effect()` for reactive data loading
|
|
493
|
+
- Responsive breakpoints (`{ '960px': '90vw', '640px': '95vw' }`)
|
|
494
|
+
|
|
452
495
|
### UserCompanyPermissionDialogComponent
|
|
453
496
|
|
|
454
497
|
Manages user-to-company assignments with checkboxes.
|
|
@@ -468,11 +511,21 @@ import { UserCompanyPermissionDialogComponent } from '@flusys/ng-auth';
|
|
|
468
511
|
})
|
|
469
512
|
```
|
|
470
513
|
|
|
514
|
+
**Inputs/Outputs:**
|
|
515
|
+
```typescript
|
|
516
|
+
readonly visible = input.required<boolean>();
|
|
517
|
+
readonly user = input.required<IUser | null>();
|
|
518
|
+
readonly closed = output<void>();
|
|
519
|
+
readonly permissionsChanged = output<void>();
|
|
520
|
+
```
|
|
521
|
+
|
|
471
522
|
**Features:**
|
|
472
523
|
- Shows all companies with checkboxes
|
|
473
524
|
- Pre-checks assigned companies
|
|
474
525
|
- Immediate save on toggle (no Save button)
|
|
475
526
|
- Toast notifications for success/error
|
|
527
|
+
- Loading spinner during data fetch
|
|
528
|
+
- Disabled checkboxes while saving
|
|
476
529
|
|
|
477
530
|
### UserBranchPermissionDialogComponent
|
|
478
531
|
|
|
@@ -494,13 +547,14 @@ import { UserBranchPermissionDialogComponent } from '@flusys/ng-auth';
|
|
|
494
547
|
```
|
|
495
548
|
|
|
496
549
|
**Features:**
|
|
497
|
-
-
|
|
498
|
-
-
|
|
550
|
+
- Company dropdown shows only user's permitted companies
|
|
551
|
+
- Branch list loads on company selection
|
|
499
552
|
- Immediate save on toggle
|
|
553
|
+
- Contextual empty messages (no permissions vs no branches)
|
|
500
554
|
- Scoped to user's permitted companies
|
|
501
555
|
|
|
502
556
|
**Workflow:**
|
|
503
|
-
1. Dialog opens → Loads user's permitted companies
|
|
557
|
+
1. Dialog opens → Loads user's permitted companies via effect()
|
|
504
558
|
2. User selects company → Loads branches for that company
|
|
505
559
|
3. User toggles branch checkboxes → Immediately assigns/revokes
|
|
506
560
|
|
|
@@ -706,6 +760,67 @@ interface IUserBranchPermission { id: string; userId: string; branchId: string;
|
|
|
706
760
|
|
|
707
761
|
## Best Practices
|
|
708
762
|
|
|
763
|
+
### Angular 21 Component Patterns
|
|
764
|
+
|
|
765
|
+
All components in ng-auth follow these patterns:
|
|
766
|
+
|
|
767
|
+
```typescript
|
|
768
|
+
@Component({
|
|
769
|
+
selector: 'lib-example',
|
|
770
|
+
standalone: true,
|
|
771
|
+
changeDetection: ChangeDetectionStrategy.OnPush, // Always OnPush
|
|
772
|
+
imports: [AngularModule, PrimeModule, FormField],
|
|
773
|
+
template: `...`
|
|
774
|
+
})
|
|
775
|
+
export class ExampleComponent {
|
|
776
|
+
// Dependency injection via inject()
|
|
777
|
+
private readonly service = inject(MyService);
|
|
778
|
+
|
|
779
|
+
// Signal-based state
|
|
780
|
+
readonly isLoading = signal(false);
|
|
781
|
+
readonly data = signal<IData | null>(null);
|
|
782
|
+
|
|
783
|
+
// Computed signals
|
|
784
|
+
readonly isValid = computed(() => this.data() !== null);
|
|
785
|
+
|
|
786
|
+
// Signal Forms
|
|
787
|
+
readonly formModel = signal<IFormModel>({ name: '', email: '' });
|
|
788
|
+
readonly formTree = form(this.formModel, (f) => {
|
|
789
|
+
required(f.name, { message: 'Name is required' });
|
|
790
|
+
});
|
|
791
|
+
|
|
792
|
+
// Error handling with proper typing
|
|
793
|
+
async onSubmit(): Promise<void> {
|
|
794
|
+
try {
|
|
795
|
+
// ...
|
|
796
|
+
} catch (error: unknown) {
|
|
797
|
+
const err = error as { error?: { message?: string } };
|
|
798
|
+
// Handle error
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
```
|
|
803
|
+
|
|
804
|
+
### Responsive Design Patterns
|
|
805
|
+
|
|
806
|
+
```html
|
|
807
|
+
<!-- Responsive container -->
|
|
808
|
+
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 p-4">
|
|
809
|
+
|
|
810
|
+
<!-- Two-column form grid -->
|
|
811
|
+
<form class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
812
|
+
|
|
813
|
+
<!-- Right-aligned buttons -->
|
|
814
|
+
<div class="flex justify-end gap-4 md:col-span-2 pt-4">
|
|
815
|
+
|
|
816
|
+
<!-- Horizontally scrollable tabs -->
|
|
817
|
+
<div class="scrollbar-hide flex gap-1 overflow-x-auto flex-nowrap">
|
|
818
|
+
|
|
819
|
+
<!-- Responsive table wrapper -->
|
|
820
|
+
<div class="overflow-x-auto">
|
|
821
|
+
<p-table [scrollable]="true" [paginator]="total() > 0">
|
|
822
|
+
```
|
|
823
|
+
|
|
709
824
|
### Component Organization
|
|
710
825
|
|
|
711
826
|
```
|
|
@@ -716,7 +831,14 @@ projects/ng-auth/
|
|
|
716
831
|
├── pages/ ← Route-specific pages
|
|
717
832
|
│ ├── login/
|
|
718
833
|
│ ├── register/
|
|
719
|
-
│
|
|
834
|
+
│ ├── forgot-password/
|
|
835
|
+
│ ├── reset-password/
|
|
836
|
+
│ ├── verify-email/
|
|
837
|
+
│ ├── profile/
|
|
838
|
+
│ ├── administration/
|
|
839
|
+
│ ├── users/
|
|
840
|
+
│ ├── company/
|
|
841
|
+
│ └── branch/
|
|
720
842
|
├── services/
|
|
721
843
|
├── guards/
|
|
722
844
|
├── interceptors/
|
|
@@ -745,6 +867,27 @@ const allCompanies = await companyApi.fetchList(); // Security risk
|
|
|
745
867
|
- TokenRefreshInterceptor handles automatic refresh
|
|
746
868
|
- Auto-persistence for company/branch selection
|
|
747
869
|
|
|
870
|
+
### Type Safety
|
|
871
|
+
|
|
872
|
+
Avoid `any` types - use explicit interfaces:
|
|
873
|
+
|
|
874
|
+
```typescript
|
|
875
|
+
// ✅ CORRECT - Explicit response type
|
|
876
|
+
.subscribe({
|
|
877
|
+
next: (result: { message?: string }) => {
|
|
878
|
+
this.messageService.add({
|
|
879
|
+
severity: 'success',
|
|
880
|
+
detail: result.message,
|
|
881
|
+
});
|
|
882
|
+
},
|
|
883
|
+
});
|
|
884
|
+
|
|
885
|
+
// ❌ WRONG - Using any
|
|
886
|
+
.subscribe({
|
|
887
|
+
next: (result: any) => { ... },
|
|
888
|
+
});
|
|
889
|
+
```
|
|
890
|
+
|
|
748
891
|
### Multi-Company Flow
|
|
749
892
|
|
|
750
893
|
1. Login → Check if `requiresSelection: true`
|
|
@@ -801,6 +944,143 @@ providers: [
|
|
|
801
944
|
|
|
802
945
|
---
|
|
803
946
|
|
|
947
|
+
## No-IAM Mode (Bypassing Permission Guards)
|
|
948
|
+
|
|
949
|
+
When you don't use the `@flusys/ng-iam` module but your routes use `permissionGuard`, the guard will fail because no permissions are loaded.
|
|
950
|
+
|
|
951
|
+
**Solution:** Set wildcard permissions in `PermissionValidatorService` after login.
|
|
952
|
+
|
|
953
|
+
### How Permission Guard Works
|
|
954
|
+
|
|
955
|
+
```
|
|
956
|
+
Route with permissionGuard('user.view')
|
|
957
|
+
↓
|
|
958
|
+
permissionGuard() → inject(PermissionValidatorService)
|
|
959
|
+
↓
|
|
960
|
+
permissionValidator.isPermissionsLoaded() → false
|
|
961
|
+
↓
|
|
962
|
+
Redirect to '/' ❌
|
|
963
|
+
```
|
|
964
|
+
|
|
965
|
+
### Fix: Set Wildcard Permissions After Login
|
|
966
|
+
|
|
967
|
+
#### Option 1: In Login Flow (Recommended)
|
|
968
|
+
|
|
969
|
+
Update your login handling to set permissions after successful authentication:
|
|
970
|
+
|
|
971
|
+
```typescript
|
|
972
|
+
// login.component.ts or auth.service.ts
|
|
973
|
+
import { PermissionValidatorService } from '@flusys/ng-shared';
|
|
974
|
+
|
|
975
|
+
@Component({ ... })
|
|
976
|
+
export class LoginComponent {
|
|
977
|
+
private readonly permissionValidator = inject(PermissionValidatorService);
|
|
978
|
+
private readonly authApi = inject(AuthApiService);
|
|
979
|
+
private readonly authState = inject(AuthStateService);
|
|
980
|
+
|
|
981
|
+
async onLogin(): Promise<void> {
|
|
982
|
+
const response = await firstValueFrom(this.authApi.login(credentials));
|
|
983
|
+
this.authState.setLoginState(response);
|
|
984
|
+
|
|
985
|
+
// When IAM is NOT used, grant all permissions via wildcard
|
|
986
|
+
this.permissionValidator.setPermissions(['*']);
|
|
987
|
+
|
|
988
|
+
// Navigate to dashboard
|
|
989
|
+
this.router.navigate(['/dashboard']);
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
```
|
|
993
|
+
|
|
994
|
+
#### Option 2: In APP_INITIALIZER
|
|
995
|
+
|
|
996
|
+
Set permissions at app startup:
|
|
997
|
+
|
|
998
|
+
```typescript
|
|
999
|
+
// app.config.ts
|
|
1000
|
+
import { APP_INITIALIZER } from '@angular/core';
|
|
1001
|
+
import { PermissionValidatorService } from '@flusys/ng-shared';
|
|
1002
|
+
|
|
1003
|
+
export const appConfig: ApplicationConfig = {
|
|
1004
|
+
providers: [
|
|
1005
|
+
{
|
|
1006
|
+
provide: APP_INITIALIZER,
|
|
1007
|
+
useFactory: (permissionValidator: PermissionValidatorService) => {
|
|
1008
|
+
return () => {
|
|
1009
|
+
// Set wildcard permissions when IAM is disabled
|
|
1010
|
+
permissionValidator.setPermissions(['*']);
|
|
1011
|
+
};
|
|
1012
|
+
},
|
|
1013
|
+
deps: [PermissionValidatorService],
|
|
1014
|
+
multi: true,
|
|
1015
|
+
},
|
|
1016
|
+
// ... other providers
|
|
1017
|
+
],
|
|
1018
|
+
};
|
|
1019
|
+
```
|
|
1020
|
+
|
|
1021
|
+
#### Option 3: In AuthInitService (After Session Restore)
|
|
1022
|
+
|
|
1023
|
+
If you restore sessions on page refresh, set permissions there:
|
|
1024
|
+
|
|
1025
|
+
```typescript
|
|
1026
|
+
// After successful /auth/me response
|
|
1027
|
+
authInit.initialize().subscribe({
|
|
1028
|
+
next: (user) => {
|
|
1029
|
+
// Session restored, set wildcard permissions
|
|
1030
|
+
permissionValidator.setPermissions(['*']);
|
|
1031
|
+
},
|
|
1032
|
+
});
|
|
1033
|
+
```
|
|
1034
|
+
|
|
1035
|
+
### Wildcard Permission Syntax
|
|
1036
|
+
|
|
1037
|
+
The `permissionGuard` utilities support these patterns:
|
|
1038
|
+
|
|
1039
|
+
| Pattern | Meaning |
|
|
1040
|
+
|---------|---------|
|
|
1041
|
+
| `*` | Full access to everything |
|
|
1042
|
+
| `user.*` | All user permissions |
|
|
1043
|
+
| `branch.*` | All branch permissions |
|
|
1044
|
+
|
|
1045
|
+
### For Jest Tests
|
|
1046
|
+
|
|
1047
|
+
Mock `PermissionValidatorService` in your test setup:
|
|
1048
|
+
|
|
1049
|
+
```typescript
|
|
1050
|
+
import { PermissionValidatorService } from '@flusys/ng-shared';
|
|
1051
|
+
|
|
1052
|
+
describe('MyComponent', () => {
|
|
1053
|
+
beforeEach(async () => {
|
|
1054
|
+
await TestBed.configureTestingModule({
|
|
1055
|
+
imports: [MyComponent],
|
|
1056
|
+
providers: [
|
|
1057
|
+
{
|
|
1058
|
+
provide: PermissionValidatorService,
|
|
1059
|
+
useValue: {
|
|
1060
|
+
permissions: () => ['*'],
|
|
1061
|
+
isPermissionsLoaded: () => true,
|
|
1062
|
+
hasPermission: () => true,
|
|
1063
|
+
setPermissions: jest.fn(),
|
|
1064
|
+
clearPermissions: jest.fn(),
|
|
1065
|
+
},
|
|
1066
|
+
},
|
|
1067
|
+
],
|
|
1068
|
+
}).compileComponents();
|
|
1069
|
+
});
|
|
1070
|
+
});
|
|
1071
|
+
```
|
|
1072
|
+
|
|
1073
|
+
### For Template Permission Checks
|
|
1074
|
+
|
|
1075
|
+
If using `*hasPermission` directive, it will also respect wildcard:
|
|
1076
|
+
|
|
1077
|
+
```html
|
|
1078
|
+
<!-- This will show when permissions include '*' -->
|
|
1079
|
+
<button *hasPermission="'user.delete'">Delete User</button>
|
|
1080
|
+
```
|
|
1081
|
+
|
|
1082
|
+
---
|
|
1083
|
+
|
|
804
1084
|
## See Also
|
|
805
1085
|
|
|
806
1086
|
- [CORE-GUIDE.md](./CORE-GUIDE.md) - APP_CONFIG configuration
|
|
@@ -811,5 +1091,25 @@ providers: [
|
|
|
811
1091
|
|
|
812
1092
|
---
|
|
813
1093
|
|
|
814
|
-
**Last Updated:** 2026-02-
|
|
1094
|
+
**Last Updated:** 2026-02-21
|
|
815
1095
|
**Angular Version:** 21
|
|
1096
|
+
|
|
1097
|
+
## Component Checklist
|
|
1098
|
+
|
|
1099
|
+
| Component | OnPush | Signal Forms | Responsive | Dark Mode |
|
|
1100
|
+
|-----------|--------|--------------|------------|-----------|
|
|
1101
|
+
| LoginPageComponent | ✅ | ✅ | ✅ | ✅ |
|
|
1102
|
+
| RegisterPageComponent | ✅ | ✅ | ✅ | ✅ |
|
|
1103
|
+
| ForgotPasswordPageComponent | ✅ | ✅ | ✅ | ✅ |
|
|
1104
|
+
| ResetPasswordPageComponent | ✅ | ✅ | ✅ | ✅ |
|
|
1105
|
+
| VerifyEmailPageComponent | ✅ | ✅ | ✅ | ✅ |
|
|
1106
|
+
| ProfilePageComponent | ✅ | ✅ | ✅ | ✅ |
|
|
1107
|
+
| AdministrationPageComponent | ✅ | N/A | ✅ | ✅ |
|
|
1108
|
+
| UserListComponent | ✅ | N/A | ✅ | ✅ |
|
|
1109
|
+
| UserFormComponent | ✅ | ✅ | ✅ | ✅ |
|
|
1110
|
+
| CompanyListComponent | ✅ | N/A | ✅ | ✅ |
|
|
1111
|
+
| CompanyFormComponent | ✅ | ✅ | ✅ | ✅ |
|
|
1112
|
+
| BranchListComponent | ✅ | N/A | ✅ | ✅ |
|
|
1113
|
+
| BranchFormComponent | ✅ | ✅ | ✅ | ✅ |
|
|
1114
|
+
| UserCompanyPermissionDialogComponent | ✅ | N/A | ✅ | ✅ |
|
|
1115
|
+
| UserBranchPermissionDialogComponent | ✅ | N/A | ✅ | ✅ |
|