Files
cbsnexora/src/app/access-control/access-control.service.ts
T
Aditya 3b752dc6ae feat: MFA/2FA, OTP audit logs, KYC + client/user enhancements
- Access Control module: SMS/Email/TOTP/Hardware-token MFA factor management
- OTP Audit Logs module under Admin menu (list + detail) with 401/403 handling
- Two-factor authentication login flow updates
- Client KYC, India address, family-members, ckyc, nominee enhancements
- New shared KYC helpers (kyc-proof-types, kyc-upload-helper.service)
- User create/edit/view enhancements
- Shell navigation: OTP Audit Logs entry under real Admin menu
2026-05-10 13:24:48 +05:30

98 lines
3.1 KiB
TypeScript

/**
* Copyright since 2025 Mifos Initiative
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
import { Injectable, inject } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';
export interface OtpGenerateResponse {
success: boolean;
otpId?: number;
otpReference?: string;
message?: string;
}
export interface OtpVerifyResponse {
verified: boolean;
message?: string;
}
export type MfaFactorType = 'SMS' | 'EMAIL' | 'TOTP' | 'TOKEN';
export interface MfaFactor {
id?: number;
factorType: MfaFactorType;
destination?: string;
secret?: string;
serial?: string;
verified: boolean;
verifiedOn?: string;
otpLogId?: number;
}
/**
* Wraps the login-OTP endpoints (LincPay SMS + SMTP email) plus the
* MFA-factor persistence endpoints used by Access Control.
*/
@Injectable({ providedIn: 'root' })
export class AccessControlService {
private http = inject(HttpClient);
// ───── Mobile OTP (LincPay) ─────
sendMobileOtp(username: string): Observable<OtpGenerateResponse> {
return this.http.post<OtpGenerateResponse>('/login/otp/generate', { username });
}
verifyMobileOtp(otpId: number, otp: string): Observable<OtpVerifyResponse> {
return this.http.post<OtpVerifyResponse>('/login/otp/verify', { otpId, otp });
}
// ───── Email OTP (SMTP) ─────
sendEmailOtp(username: string): Observable<OtpGenerateResponse> {
return this.http.post<OtpGenerateResponse>('/login/otp/email/generate', { username });
}
verifyEmailOtp(otpId: number, otp: string): Observable<OtpVerifyResponse> {
return this.http.post<OtpVerifyResponse>('/login/otp/email/verify', { otpId, otp });
}
// ───── MFA factors persistence (NEW backend endpoints) ─────
/**
* Marker header that tells our Fineract security filter to return a plain
* 401 (no `WWW-Authenticate: Basic`) so the browser doesn't pop up its
* native Basic-Auth dialog when the endpoint isn't wired up yet.
*/
private silentHeaders = new HttpHeaders({ 'X-Requested-With': 'XMLHttpRequest' });
/** List the signed-in user's MFA factors. */
listMyFactors(): Observable<MfaFactor[]> {
return this.http.get<MfaFactor[]>('/mfa/factors', { headers: this.silentHeaders });
}
/**
* Persist a verified factor. Backend should set `verified=true`,
* `verified_on=NOW()`, and link to the matching `m_otp_log` row.
*/
saveFactor(payload: {
factorType: MfaFactorType;
destination?: string;
secret?: string;
serial?: string;
otpId?: number;
}): Observable<MfaFactor> {
return this.http.post<MfaFactor>('/mfa/factors', payload, { headers: this.silentHeaders });
}
/** Disable a factor (logical delete — sets `disabled_on`). */
disableFactor(factorId: number): Observable<{ resourceId: number }> {
return this.http.delete<{ resourceId: number }>(`/mfa/factors/${factorId}`, {
headers: this.silentHeaders
});
}
}