September 20, 2026 · 2 min read
OIDC in Fineract: stop managing passwords in the core
How to delegate Apache Fineract authentication to an identity provider (Keycloak, Entra ID, Google) with OpenID Connect, what changes in the API and what does not change in permissions.
Fineract ships with two authentication modes: Basic (its own usernames and passwords, stored in m_appuser) and OAuth2, where the core validates a token issued by an external identity provider. The first is convenient for a demo. The second is the only defensible option for a regulated institution: MFA, password policy, lockout, session audit and user offboarding live in the identity provider, not in a table inside the core.
What Fineract does in OAuth2 mode
With FINERACT_SECURITY_OAUTH_ENABLED=true and FINERACT_SECURITY_BASICAUTH_ENABLED=false, Fineract stops accepting the Authorization: Basic header and behaves as a resource server: every API call must carry Authorization: Bearer <JWT>, and the JWT is validated against the configured issuer (SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI). Fineract fetches the issuer’s JWKS, verifies signature, expiry and issuer, and resolves the AppUser from the user claim.
Two things to be clear about before flipping the switch:
- Authorization stays in Fineract. Roles and permissions (
m_role,m_permission) are still assigned in the core. OIDC answers who this is; Fineract answers what they can do. Syncing IdP groups to Fineract roles is a separate project, and for most institutions not worth it: the core’s permission catalog is far too granular to map onto directory groups. - The user must exist in
m_appuserwith the sameusernamethe token carries. If the user claim is the email address and core users were created with a short login, nobody gets in. Decide the convention before migrating, not after.
Minimal setup with Keycloak
In Keycloak: one realm for the institution, one confidential client for the front end (Mifos Web App or your own portal) and, for machine-to-machine integrations, one client credentials client per system. The token’s audience must include the identifier Fineract expects; otherwise validation fails with an unexplained 401.
Relevant Fineract environment variables:
FINERACT_SECURITY_BASICAUTH_ENABLED=false
FINERACT_SECURITY_OAUTH_ENABLED=true
SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI=https://idp.example.com/realms/bank
The issuer must be reachable from the Fineract container (not just from the user’s browser). In private-network deployments this is mistake number one: the JWKS never downloads and every token is invalid.
What changes for integrations
Systems that call Fineract today with a technical user and password must move to client credentials: they request a token from the IdP with their client_id and client_secret, cache it until just before expiry and renew it. A small code change and a large posture change: the secret rotates in the IdP, access is revoked in one click, and every system is identifiable in the audit trail.
Fineract’s internal jobs (COB, interest accrual) are unaffected: they run inside the process and never touch the API.
Recommended migration order
- Enable OAuth2 in a test environment with Basic still on, and validate the web app login.
- Create the IdP users with the exact
m_appuserusername; enroll MFA. - Migrate integrations to client credentials one at a time.
- Disable Basic in production during a window with rollback ready: rollback is one environment variable.
What not to do: keep Basic on “just in case” after go-live. As long as it exists, it is the door nobody watches.