Aman-Mittal opened a new issue, #369:
URL: https://github.com/apache/fineract-backoffice-ui/issues/369

   ## Summary
   
   `settings/two-factor-config.component.ts` lets an administrator configure 
two-factor
   authentication. Nothing in the login flow honours it. Turn it on and **every 
screen breaks**:
   sign-in still appears to succeed, and then every request the application 
makes returns 403.
   
   The application ships the switch for a feature it cannot then survive.
   
   ## Reproduced against a real Fineract
   
   Restart the e2e backend with 2FA on:
   
   ```yaml
   # deploy/docker-compose-e2e.yml → fineract-backend → environment
   - FINERACT_SECURITY_2FA_ENABLED=true
   ```
   
   Then, authenticated as `mifos` (who holds `ALL_FUNCTIONS`):
   
   | Request | 2FA off | 2FA on |
   |---|---|---|
   | `GET /v1/offices` | 200 | **403** |
   | `GET /v1/clients` | 200 | **403** |
   | `GET /v1/loans` | 200 | **403** |
   | `GET /v1/glaccounts` | 200 | **403** |
   | `GET /v1/twofactor/configure` | 404 | **403** |
   | `GET /v1/twofactor` | 404 | **200** |
   
   Only `/v1/twofactor` answers. Everything else is refused until a second 
factor is validated —
   including the configuration screen the application already has.
   
   And `POST /v1/authentication` grows a field the client never reads:
   
   ```jsonc
   {
     "username": "mifos",
     "authenticated": true,          // ← the UI believes it is done here
     "base64EncodedAuthenticationKey": "…",
     "permissions": ["ALL_FUNCTIONS", …],
     "shouldRenewPassword": false,
     "isTwoFactorAuthenticationRequired": true   // ← never inspected
   }
   ```
   
   `AuthService.login()` stores the session and reports success on 
`authenticated: true`. The user
   lands on the dashboard, every widget 403s, and — since #368 — those 403s are 
correctly reported as
   "you do not have permission". Which is *true*, and completely unactionable: 
the user holds every
   permission Fineract defines. There is no way forward from that screen and no 
way to reach the
   configuration page to turn it back off, because that 403s too.
   
   ## Business Value
   
   - **A configuration screen that bricks the product is worse than no screen 
at all.** An
     administrator enabling 2FA — a control their own security review very 
likely asked for — takes
     the institution offline, with no in-product way back. The recovery is a 
backend restart with an
     environment variable, which is not something a branch can do.
   - **2FA is table stakes for a security review of a core-banking back 
office.** Its absence is a
     finding on its own; shipping the configuration for it while the login 
cannot honour it is a
     worse finding, because it reads as an implemented control that does not 
work.
   - **The endpoints already exist and are already in the generated client.** 
The remaining work is a
     login step and one header, not a platform integration.
   
   ## The contract
   
   All six operations are already generated. Four in `TwoFactorService`:
   
   | Operation | Endpoint | Purpose |
   |---|---|---|
   | `getTwofactor()` | `GET /v1/twofactor` | delivery methods available to 
this user |
   | `postTwofactor(deliveryMethod, extendedToken)` | `POST /v1/twofactor` | 
send the one-time token |
   | `postTwofactorValidate()` | `POST /v1/twofactor/validate` | exchange the 
token for a TFA token |
   | `postTwofactorInvalidate()` | `POST /v1/twofactor/invalidate` | end the 
second factor |
   
   and two on `DefaultService` — `getTwofactorConfigure()` / 
`putTwofactorConfigure()`
   (`GET`/`PUT /v1/twofactor/configure`), which the existing settings screen 
already calls.
   
   `GET /v1/twofactor` on the seeded tenant returns:
   
   ```json
   [{ "name": "email", "target": "[email protected]" }]
   ```
   
   The validated token then travels on subsequent requests. That header is 
**not** currently set by
   `auth.interceptor.ts`, which only attaches `Authorization`.
   
   ## Scope
   
   - `AuthService.login()` must branch on `isTwoFactorAuthenticationRequired` 
rather than treat
     `authenticated: true` as the end of the flow.
   - A second-factor step in the login feature: choose a delivery method, 
request the token, submit
     it, handle expiry and a wrong code without dropping the first factor.
   - The validated TFA token joins the session and is sent on every subsequent 
request, alongside the
     existing `Authorization` header, from the same interceptor.
   - Sign-out should invalidate it (`POST /v1/twofactor/invalidate`), not just 
drop it locally.
   - The existing settings screen should be reachable *after* the second 
factor, and should say
     plainly what enabling this does — it is currently a switch with no warning 
attached.
   - A deployment with 2FA off must behave exactly as it does today. That is 
the regression risk.
   
   ## Tests — required, not optional
   
   **Unit.** `auth.service.spec.ts` and the login component: 
`isTwoFactorAuthenticationRequired` true
   and false; a wrong token; an expired token; the session carrying the TFA 
token; sign-out
   invalidating it. The interceptor spec must cover the header being attached 
when a TFA token exists
   and absent when it does not.
   
   **E2E, mocked** — a new spec beside `e2e/rbac-route-protection.spec.ts`, 
mocking
   `POST /v1/authentication` to return `isTwoFactorAuthenticationRequired: 
true`. This is where the
   matrix belongs, because it needs no mail server: the second-factor step 
appears, a wrong code is
   rejected without losing the session, a correct one lands on the dashboard, 
and with the flag absent
   the login flow is unchanged.
   
   **E2E, real backend** — one spec proving the flow against Fineract with
   `FINERACT_SECURITY_2FA_ENABLED=true`. Two things make this awkward and both 
have answers:
   
   - The stack has no SMTP, so `POST /v1/twofactor?deliveryMethod=email` 
returns **500**. The token is
     written to `twofactor_access_token` (`token`, `appuser_id`, `valid_from`, 
`valid_to`, `enabled`)
     regardless — read it the way the suite already reaches the database, 
rather than adding a mail
     catcher.
   - 2FA is process-wide, so it cannot be toggled per test. It needs its own 
compose override and its
     own project in `playwright.config.ts`, kept out of the default run.
   
   **Regression.** The full suite must stay green with 2FA off. That is the 
case that protects every
   existing deployment.
   
   ## Documentation
   
   - `DOCS/` — how the second factor works, what the TFA token is, how to 
enable it locally, and how
     to run the real-backend spec. `DOCS/RBAC.md` is the model for the level of 
detail.
   - The security policy should record 2FA as supported once it is, since its 
absence is currently a
     reasonable finding against the product.
   - A migration note: deployments that have *already* enabled 2FA on the 
backend are broken today and
     will be fixed by this, which is worth saying out loud rather than leaving 
to be discovered.
   
   ## Out of scope
   
   OIDC / external identity login, which is a separate gap with a separate 
failure mode. Filed
   separately.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to