From afc8f14597cb6e0478646e45d2f10a2e12b09d19 Mon Sep 17 00:00:00 2001 From: kanishksingh23 Date: Tue, 14 Jul 2026 11:21:51 +0000 Subject: [PATCH] WEB-1032: fix 2FA email OTP not working in login The two-factor-authentication component uses ChangeDetectionStrategy.OnPush but never calls markForCheck() after its asynchronous state updates, so the view was never refreshed: - Delivery methods loaded via getDeliveryMethods().subscribe() were never rendered, so the login screen showed "Please select a delivery method:" with no radio options and a permanently-disabled "Request OTP" button. - otpRequested / tokenValidityTime set after requestOTP() never revealed the OTP entry field. - loading / resendOTPLoading spinner states were also not reflected. Inject ChangeDetectorRef and call markForCheck() after each async state mutation (delivery-method load, requestOTP, validateOTP, resendOTP). Verified end-to-end against the 2fa.mifos.community backend and a local Fineract instance: the email delivery method now renders, the Request OTP button enables on selection, and the OTP entry field appears. --- .../two-factor-authentication.component.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/app/login/two-factor-authentication/two-factor-authentication.component.ts b/src/app/login/two-factor-authentication/two-factor-authentication.component.ts index 57cfc9d2b1..5feb6ff970 100644 --- a/src/app/login/two-factor-authentication/two-factor-authentication.component.ts +++ b/src/app/login/two-factor-authentication/two-factor-authentication.component.ts @@ -7,7 +7,7 @@ */ /** Angular Imports */ -import { ChangeDetectionStrategy, Component, OnInit, inject } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit, inject } from '@angular/core'; import { FormGroup, FormBuilder, Validators, ReactiveFormsModule } from '@angular/forms'; /** rxjs Imports */ @@ -44,6 +44,7 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; export class TwoFactorAuthenticationComponent implements OnInit { private formBuilder = inject(FormBuilder); private authenticationService = inject(AuthenticationService); + private changeDetectorRef = inject(ChangeDetectorRef); /** Available delivery methods to receive OTP. */ twoFactorAuthenticationDeliveryMethods: any; @@ -71,6 +72,8 @@ export class TwoFactorAuthenticationComponent implements OnInit { this.createTwoFactorAuthenticationDeliveryMethodForm(); this.authenticationService.getDeliveryMethods().subscribe((deliveryMethods: any) => { this.twoFactorAuthenticationDeliveryMethods = deliveryMethods; + // OnPush: the delivery methods arrive asynchronously, so notify the view to render the options. + this.changeDetectorRef.markForCheck(); }); } @@ -92,12 +95,15 @@ export class TwoFactorAuthenticationComponent implements OnInit { // Angular Material Bug: Validation errors won't get removed on reset. this.twoFactorAuthenticationDeliveryMethodForm.enable(); this.loading = false; + this.changeDetectorRef.markForCheck(); }) ) .subscribe((response: any) => { this.createTwoFactorAuthenticationForm(); this.otpRequested = true; this.tokenValidityTime = response.tokenLiveTimeInSec; + // OnPush: reveal the OTP entry field once the OTP has been requested. + this.changeDetectorRef.markForCheck(); }); } @@ -116,6 +122,7 @@ export class TwoFactorAuthenticationComponent implements OnInit { // Angular Material Bug: Validation errors won't get removed on reset. this.twoFactorAuthenticationForm.enable(); this.loading = false; + this.changeDetectorRef.markForCheck(); }) ) .subscribe(); @@ -136,6 +143,7 @@ export class TwoFactorAuthenticationComponent implements OnInit { // Angular Material Bug: Validation errors won't get removed on reset. this.twoFactorAuthenticationForm.enable(); this.resendOTPLoading = false; + this.changeDetectorRef.markForCheck(); }) ) .subscribe();