From d94e715957c125caebe69a3849b19e74b52ee479 Mon Sep 17 00:00:00 2001 From: Mazdak Gibran <141390141+mazdakgibran@users.noreply.github.com> Date: Thu, 22 Jan 2026 11:57:44 +0500 Subject: [PATCH] first time login first time login logic and validations, --- src/app/app.routes.ts | 2 +- .../authenticate/login/login.component.html | 6 +- src/app/authenticate/login/login.component.ts | 10 ++ src/app/services/authenticate.service.ts | 85 +++++++------- src/app/shared/guards/authentication.guard.ts | 30 ++++- .../change-password.component.html | 45 +++++-- .../change-password.component.ts | 111 +++++++++++++++++- .../reset-password.component.ts | 4 +- src/assets/i18n/English.json | 3 +- 9 files changed, 232 insertions(+), 64 deletions(-) diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index 262adc0..0fa3266 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -11,7 +11,7 @@ export const routes: Routes = [ component: LoginComponent }, { - path: 'changepassword', + path: 'first-login-change-password', component: ChangePasswordComponent }, { diff --git a/src/app/authenticate/login/login.component.html b/src/app/authenticate/login/login.component.html index a543a1c..d75d8a7 100644 --- a/src/app/authenticate/login/login.component.html +++ b/src/app/authenticate/login/login.component.html @@ -23,6 +23,10 @@
Logo
+

+ aConnect +

+
@@ -46,7 +50,7 @@
- +
diff --git a/src/app/authenticate/login/login.component.ts b/src/app/authenticate/login/login.component.ts index c550208..cc41c09 100644 --- a/src/app/authenticate/login/login.component.ts +++ b/src/app/authenticate/login/login.component.ts @@ -104,7 +104,17 @@ export class LoginComponent { this.ucred.password = this.loginForm.get(FormConstants.PASSWORD)?.value; this.ucred.userId = this.loginForm.get(FormConstants.USER_ID)?.value; this.authService.authenticate(this.ucred).subscribe( (res: any) => { + const user = res?.user; + this.storageService.setItem('user', JSON.stringify(res)); + if (res?.requiresPasswordChange || user?.firstLogin) { + console.log('First time login - redirecting to first-login-change-password'); + this.router.navigate(['/first-login-change-password']); + } else { + console.log('Regular login - redirecting to dashboard'); + this.router.navigate(['/home/dashboard']); + } + }); } diff --git a/src/app/services/authenticate.service.ts b/src/app/services/authenticate.service.ts index 52b6a7d..40f1dfb 100644 --- a/src/app/services/authenticate.service.ts +++ b/src/app/services/authenticate.service.ts @@ -23,53 +23,52 @@ export class AuthenticationService { constructor(private buttonManagementService: ButtonManagementService, private httpService: HttpURIService, private router: Router, private credentialService: CredentialService, private i18nService: I18NService, private storageService: StorageService) { } + authenticate(uCreds: UserCredentials): Observable { + const userJson = this.storageService.getItem('user'); + if (this.storageService.getItem('user') != null) { + this.i18nService.error(ErrorMessages.ALREADY_LOGGED_IN, []); + return new Observable(); // empty + } - authenticate(uCreds: UserCredentials) : Observable { - const observable = new Observable((observer: Observer) => { - - if (this.storageService.getItem('user') != null) { - this.i18nService.error(ErrorMessages.ALREADY_LOGGED_IN,[]); - return; - } - this.credentialService.setPorOrgacode(HiddenValues.POR_ORGACODE); - this.credentialService.setUserId(uCreds.userId); - this.credentialService.setPassword(uCreds.password); - this.storageService.setItem(FormConstants.POR_ORGACODE, HiddenValues.POR_ORGACODE); - this.storageService.setItem(FormConstants.USER_ID, uCreds.userId); - this.storageService.setItem(FormConstants.PASSWORD, uCreds.password); - this.httpService.requestPOST(URIKey.USER_LOGIN_URI, uCreds).subscribe((data: any) => { - if (!(data instanceof HttpErrorResponse)) { - data.authenticated = true; - this.i18nService.success(SuccessMessages.LOGIN_SUCCESSFULLY, []); - this.storageService.setItem('user', JSON.stringify(data)); - this.credentialService.setToken(data.token); - this.credentialService.setUserType(data.role); - if(data.user.permissions){ - this.storageService.setItem('permission', data.user.permissions); - this.credentialService.setPermission(JSON.parse(data.user.permissions)); - } - else{ - this.storageService.setItem('permission', '[]'); - this.credentialService.setPermission([]); - } - this.buttonManagementService.setButtonPermissions(this.credentialService.getPermission(), this.isAdminUser()); - if(data.user.isFirstLogin){ - this.router.navigate(["/changepassword"]); - } else { - this.router.navigate(["/home/dashboard"]); - } - this.onAuthenticationComplete.next(true); - observer.complete(); - } - else { - this.onAuthenticationComplete.next(false); - observer.error(false); + this.credentialService.setPorOrgacode(HiddenValues.POR_ORGACODE); + this.credentialService.setUserId(uCreds.userId); + this.credentialService.setPassword(uCreds.password); + this.storageService.setItem(FormConstants.POR_ORGACODE, HiddenValues.POR_ORGACODE); + this.storageService.setItem(FormConstants.USER_ID, uCreds.userId); + this.storageService.setItem(FormConstants.PASSWORD, uCreds.password); + + return this.httpService.requestPOST(URIKey.USER_LOGIN_URI, uCreds).pipe( + tap((data: any) => { + if (!(data instanceof HttpErrorResponse)) { + data.authenticated = true; + this.storageService.setItem('user', JSON.stringify(data)); + this.credentialService.setToken(data.token); + this.credentialService.setUserType(data.role); + + if (data.user.permissions) { + this.storageService.setItem('permission', data.user.permissions); + this.credentialService.setPermission(JSON.parse(data.user.permissions)); + } else { + this.storageService.setItem('permission', '[]'); + this.credentialService.setPermission([]); } - }); - }); - return observable; + this.buttonManagementService.setButtonPermissions(this.credentialService.getPermission(), this.isAdminUser()); + } + }) + ); +} + + updateCredentialsAfterPasswordChange(newPassword: string) { + this.storageService.setItem(FormConstants.PASSWORD, newPassword); + this.credentialService.setPassword(newPassword); + const userStr = this.storageService.getItem('user'); + if (userStr) { + const user = JSON.parse(userStr); + user.authenticated = true; + this.storageService.setItem('user', JSON.stringify(user)); } +} isAuthenticated(): boolean { if (this.storageService && this.storageService.getItem('user') != null) { diff --git a/src/app/shared/guards/authentication.guard.ts b/src/app/shared/guards/authentication.guard.ts index fdcb525..c925c4e 100644 --- a/src/app/shared/guards/authentication.guard.ts +++ b/src/app/shared/guards/authentication.guard.ts @@ -6,6 +6,7 @@ import { AuthenticationService } from '../../services/authenticate.service'; import { CredentialService } from '../../services/credential.service'; import { FormConstants } from '../../utils/enums'; import { ButtonManagementService } from '../../services/button-management.service'; +import { StorageService } from '../services/storage.service'; @Injectable( @@ -13,26 +14,47 @@ import { ButtonManagementService } from '../../services/button-management.servic ) export class AuthenticationGuard implements CanActivate { - constructor(private router: Router, private authService: AuthenticationService, private location: LocationStrategy, private credentialService: CredentialService,private buttonManagementService: ButtonManagementService) { } + constructor(private router: Router, private authService: AuthenticationService, private location: LocationStrategy, private credentialService: CredentialService,private buttonManagementService: ButtonManagementService, private storageService: StorageService) { } canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { + + if (state.url.includes('first-login-change-password')) { + return true; + } if (typeof window !== 'undefined' && window.localStorage) { - let data = JSON.parse(window.localStorage.getItem('user') || '{}') as AuthenticationResponse; - let permission = JSON.parse(window.localStorage.getItem('permission') || '[]'); + const userStr = this.storageService.getItem('user'); + if (!userStr) { + this.authService.logout(); + return false; + } + const data = JSON.parse(userStr); + + if ((data?.requiresPasswordChange || data?.user?.firstLogin) && + !state.url.includes('changePassword')) { + this.router.navigate(['/first-login-change-password']); + return false; + } + if (this.authService.isAuthenticated()) { + this.credentialService.setPorOrgacode(window.localStorage.getItem(FormConstants.POR_ORGACODE) || ''); this.credentialService.setUserId(window.localStorage.getItem(FormConstants.USER_ID) || ''); this.credentialService.setPassword(window.localStorage.getItem(FormConstants.PASSWORD) || ''); this.credentialService.setToken(data.token); this.credentialService.setUserType(data.user.role); + + let permission = JSON.parse(window.localStorage.getItem('permission') || '[]'); this.credentialService.setPermission(permission); this.buttonManagementService.setButtonPermissions(this.credentialService.getPermission(), this.authService.isAdminUser()); this.authService.onAuthenticationComplete.next(true); return true; - } else { + } + else { this.authService.logout(); return false; } + + } return false; } diff --git a/src/app/user-management/change-password/change-password.component.html b/src/app/user-management/change-password/change-password.component.html index 36762c2..4009000 100644 --- a/src/app/user-management/change-password/change-password.component.html +++ b/src/app/user-management/change-password/change-password.component.html @@ -16,16 +16,19 @@
-
+ +
-
+ *ngIf="firstTimeLoginForm.get('oldPassword')?.touched && firstTimeLoginForm.get('oldPassword')?.invalid"> {{ 'fieldRequired' | translate }}
@@ -34,12 +37,27 @@ -
+
+
- + +
+
+
+ {{ 'fieldRequired' | translate }} +
+
+ {{ 'passwordPattern' | translate }} +
+
+ {{'oldAndNewPass' | translate}} +
+ +
@@ -48,12 +66,23 @@ -
+
+
- +
+
+
+ {{ 'fieldRequired' | translate }} +
+ +
+ {{ 'passwordsDoNotMatch' | translate }} +
+
@@ -155,7 +184,7 @@ {{ 'passwordPattern' | translate }}
- Old password and new password cannot be the same + {{'oldAndNewPass' | translate}}
diff --git a/src/app/user-management/change-password/change-password.component.ts b/src/app/user-management/change-password/change-password.component.ts index 1b10c47..ecc6ccf 100644 --- a/src/app/user-management/change-password/change-password.component.ts +++ b/src/app/user-management/change-password/change-password.component.ts @@ -8,7 +8,10 @@ import { URIKey } from '../../utils/uri-enums'; import { StorageService } from '../../shared/services/storage.service'; import { I18NService } from '../../services/i18n.service'; import { HttpErrorResponse } from '@angular/common/http'; -import { SuccessMessages } from '../../utils/enums'; +import { FormConstants, SuccessMessages } from '../../utils/enums'; +import { Router } from '@angular/router'; +import { AuthenticationService } from '../../services/authenticate.service'; + @Component({ selector: 'app-change-password', @@ -31,7 +34,13 @@ passwordType2: string = 'password'; @ViewChild('psh') passwordHideShow?: PasswordHideShowComponent; @ViewChild('psh1') passwordHideShow1 ?: PasswordHideShowComponent; @ViewChild('psh2') passwordHideShow2 ?: PasswordHideShowComponent; -constructor(private fb: FormBuilder, private httpURIService: HttpURIService, private storageService: StorageService, private i18nService: I18NService){} +constructor( + private fb: FormBuilder, + private httpURIService: HttpURIService, + private storageService: StorageService, + private i18nService: I18NService, + private router: Router, + private authService: AuthenticationService ){} togglePasswordType() { this.passwordType = this.passwordHideShow?.showPassword ? 'password' : 'text'; @@ -44,8 +53,12 @@ constructor(private fb: FormBuilder, private httpURIService: HttpURIService, pri } ngOnInit(): void { - this.checkIfFirstTimeChangePasswordOrNot(); - + const userStr = this.storageService.getItem('user'); + if (userStr) { + const user = JSON.parse(userStr); + this.isFirstLogin = user?.requiresPasswordChange || user?.user?.firstLogin || false; + } + if (this.isFirstLogin) { this.firstTimeLoginForm = this.fb.group({ oldPassword: ['', Validators.required], @@ -61,7 +74,10 @@ constructor(private fb: FormBuilder, private httpURIService: HttpURIService, pri Validators.maxLength(20), Validators.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]+$/) ]] - }, { validators: this.passwordMatchValidator }); + }, { validators: [ + this.passwordMatchValidator, + this.oldAndNewPasswordNotSame,] + }); } else { @@ -135,6 +151,35 @@ constructor(private fb: FormBuilder, private httpURIService: HttpURIService, pri }; } onSubmit(){ + if (this.isFirstLogin) { + this.changeFirstTimePassword(); + } else { + this.changeRegularPassword(); + } + const payload = this.getFormPayload(); + + this.httpURIService.requestPOST(URIKey.CHANGE_PASSWORD_URI, payload) + .subscribe({ + next: (response) => { + if (!(response instanceof HttpErrorResponse)) { + this.i18nService.success(SuccessMessages.CHANGE_PASSWORD_SUCCESS, []); + this.router.navigate(['/dashboard']); + } + } + }); + + + } + + changeFirstTimePassword() { + if (!this.firstTimeLoginForm || this.firstTimeLoginForm.invalid) { + if (this.firstTimeLoginForm) { + this.markFormGroupTouched(this.firstTimeLoginForm); + } + return; + } + + const payload = this.getFormPayload(); this.httpURIService.requestPOST(URIKey.CHANGE_PASSWORD_URI, payload) @@ -142,11 +187,67 @@ constructor(private fb: FormBuilder, private httpURIService: HttpURIService, pri next: (response) => { if (!(response instanceof HttpErrorResponse)) { this.i18nService.success(SuccessMessages.CHANGE_PASSWORD_SUCCESS, []); + + const userStr = this.storageService.getItem('user'); + if (userStr) { + const user = JSON.parse(userStr); + user.requiresPasswordChange = false; + if (user.user) { + user.user.firstLogin = false; + } + + if (payload.newPassword) { + this.storageService.setItem(FormConstants.PASSWORD, payload.newPassword); + } + this.storageService.setItem('user', JSON.stringify(user)); + this.authService.updateCredentialsAfterPasswordChange(payload.newPassword); + + } + + this.router.navigate(['/home/dashboard']); } + }, + error: (error) => { + console.error('Password change failed:', error); } }); + } + + changeRegularPassword() { + if (!this.changePasswordForm || this.changePasswordForm.invalid) { + if (this.changePasswordForm) { + this.markFormGroupTouched(this.changePasswordForm); + } + return; + } + const payload = this.getFormPayload(); + + this.httpURIService.requestPOST(URIKey.CHANGE_PASSWORD_URI, payload) + .subscribe({ + next: (response) => { + if (!(response instanceof HttpErrorResponse)) { + this.i18nService.success(SuccessMessages.CHANGE_PASSWORD_SUCCESS, []); + this.router.navigate(['/home/dashboard']); + } + }, + error: (error) => { + + console.error('Password change failed:', error); + if (error.status === 401) { + this.authService.logout(); + } + } + }); + } + private markFormGroupTouched(formGroup: FormGroup) { + Object.values(formGroup.controls).forEach(control => { + control.markAsTouched(); + if (control instanceof FormGroup) { + this.markFormGroupTouched(control); + } + }); } } diff --git a/src/app/user-management/reset-password/reset-password.component.ts b/src/app/user-management/reset-password/reset-password.component.ts index 767a4d3..99785ad 100644 --- a/src/app/user-management/reset-password/reset-password.component.ts +++ b/src/app/user-management/reset-password/reset-password.component.ts @@ -9,6 +9,7 @@ import { StorageService } from '../../shared/services/storage.service'; import { I18NService } from '../../services/i18n.service'; import { ErrorMessages, SuccessMessages } from '../../utils/enums'; import { HttpErrorResponse } from '@angular/common/http'; +import { Router } from '@angular/router'; @Component({ selector: 'app-reset-password', @@ -24,7 +25,7 @@ export class ResetPasswordComponent implements OnInit{ @ViewChild('psh1') passwordHideShow1?: PasswordHideShowComponent; @ViewChild('psh2') passwordHideShow2?: PasswordHideShowComponent; - constructor(private fb: FormBuilder, private httpURIService: HttpURIService, private storageService: StorageService, private i18nService: I18NService){} + constructor(private fb: FormBuilder, private httpURIService: HttpURIService, private storageService: StorageService, private i18nService: I18NService, private router: Router){} ngOnInit(): void { const userIdValue = this.storageService.getItem('USER_ID') @@ -93,6 +94,7 @@ export class ResetPasswordComponent implements OnInit{ next: (response) => { if (!(response instanceof HttpErrorResponse)) { this.i18nService.success(SuccessMessages.RESET_PASSWORD_SUCCESS, []); + this.router.navigate(['/dashboard']); } } }); diff --git a/src/assets/i18n/English.json b/src/assets/i18n/English.json index c30915b..6a4f171 100644 --- a/src/assets/i18n/English.json +++ b/src/assets/i18n/English.json @@ -265,5 +265,6 @@ "nameMinLength" : "Name must be at least 5 characters", "emptySpaceRestriction" : "Empty spaces are not allowed", "USER_CREATED_SUCCESS": "User Created", - "USER_DELETE_SUCCESS": "User Deleted" + "USER_DELETE_SUCCESS": "User Deleted", + "oldAndNewPass":"Old password and new password cannot be the same" } \ No newline at end of file