SSO for Apache Fineract: OAuth2, OIDC, and the multi-tenant catch
6/15/2026

Sooner or later someone in security asks the question: can we put Fineract behind our corporate login? No more local passwords, no more separate user database, everyone signs in through Keycloak or Entra or Google like they do for everything else. The answer is yes, and the path is shorter than people fear. But Fineract's OAuth support is narrower and more particular than the word "SSO" suggests, and the gap between what people expect and what is actually there is where every wasted afternoon comes from.
We run managed Fineract behind corporate identity providers, so we have wired this up more than a few times. Here is the honest shape of it: what Fineract's OAuth actually does, the exact switches, the one mapping detail that causes nearly every failed attempt, and the multi-tenant catch that nobody warns you about until you are halfway in.
First, what Fineract's OAuth actually is
This is the misconception that wastes the most time, so clear it first. Fineract is a resource server, not an authorization server. It does not show anyone a login page, it does not run a "Sign in with Microsoft" button, and it does not issue tokens. All it does is check a token you already have.
The flow looks like this. Your front end, or whatever client is calling the API, sends the user to your identity provider to log in. The IdP authenticates them and hands back a signed JSON Web Token. Your client then calls Fineract with that token in an Authorization: Bearer ... header, and Fineract verifies the signature against the IdP's public keys, confirms it has not expired, and if it checks out, treats the call as authenticated. That is the whole job. The login experience, the redirect dance, the "remember me" box, all of that lives in your application and your IdP, not in Fineract.
So when you read "Fineract supports OAuth," read it as "Fineract can validate tokens your IdP issues." You bring the identity provider. Keycloak is the one the project documents and the one everyone actually runs in production; Entra and Google work too, with caveats we will get to.
Turning it on
Out of the box, Fineract uses basic authentication, the username and password your staff already have in the platform. OAuth is opt-in, and you switch from one to the other. With Docker, it is three environment variables:
FINERACT_SECURITY_BASICAUTH_ENABLED=false
FINERACT_SECURITY_OAUTH_ENABLED=true
FINERACT_SERVER_OAUTH_RESOURCE_URL=http://your-keycloak/realms/fineractThe first two flip the scheme. The third is the important one: it points Fineract at your realm. From that URL Fineract discovers the IdP's published keys (at the standard /.well-known/openid-configuration path) and uses them to check the signature on every incoming token. There is no shared secret to copy around and no network call on each request once the keys are cached; the validation is local and fast.
One small trap to save you a confused half hour: the environment variable is FINERACT_SECURITY_OAUTH_ENABLED, but if you configure it as a JVM property instead of an env var, the property is spelled fineract.security.oauth2.enabled, with a 2. The env var has no 2; the property does. They are the same switch under two names, and the mismatch is exactly the kind of thing you stare at for twenty minutes.
The mapping that causes every 401
Here is the part that turns a fifteen-minute setup into a day of head-scratching, and almost everyone hits it. You enable OAuth, you point Fineract at Keycloak, you get a token, you call the API, and you get a flat 401 Unauthenticated. The token is valid. The signature checks out. And it still fails.
The reason is how Fineract decides which user a token belongs to. It reads the token's sub claim, the subject, and looks for a Fineract user whose username matches it exactly. No sub matching an existing username means no user, which means 401, no matter how perfect the token is otherwise.
Two things follow from that. First, the user has to already exist in Fineract. There is no auto-provisioning in the shipped releases: a token for someone who is not in the platform's user table will not create them, it will just be rejected. You provision your people in Fineract first, then let them sign in through the IdP. Second, and this is the specific thing that bites Keycloak users, Keycloak by default puts a random UUID in the sub claim, not the username. So the token's subject is a string of hex that matches no one, and every request 401s. The fix is one mapper in Keycloak: map the user's username property into the sub claim, so the subject Fineract reads is the actual username it is looking for. Add that mapper and the 401s evaporate. Miss it and you will swear the whole thing is broken.
What the IdP does, and what it does not
It is worth being precise about the division of labour, because it shapes what SSO buys you here. The identity provider authenticates: it proves the person is who they say they are. Authorization, what that person is allowed to do inside Fineract, stays entirely inside Fineract. Roles and permissions live in Fineract's own tables, attached to that user, exactly as they would under basic auth. The token gets someone in the door; Fineract's own role model decides which rooms they can enter.
That means moving to SSO does not let you manage Fineract permissions from Keycloak. Group-to-role mapping, where your IdP groups drive Fineract permissions, is not how the shipped integration works. You still administer roles in Fineract. If that sounds like a limitation, it is also a feature: for a core banking system, you usually want access rights governed and audited inside the system of record, not delegated to whoever administers the directory.
The multi-tenant catch
Now the part that nobody warns you about, and the reason this post exists. Fineract is multi-tenant: one deployment serves many institutions, and every API call carries a Fineract-Platform-TenantId header that selects which one. That works beautifully for everything, until you reach OAuth.
The catch is that the resource URL, the realm Fineract validates tokens against, is a single value for the entire instance. One issuer, one realm, for every tenant on the box. So out of the box, every institution you host has to share the same realm in the same identity provider, with all of their users mixed together and only a tenant identifier to tell them apart. You still pass the tenant header by hand on every call; the token does not carry the tenant, and the realm cannot vary by tenant.
For a single organisation running its own Fineract, that is completely fine: you have one realm anyway. But if you host multiple institutions, or your customers each want to bring their own identity provider, a single shared realm does not hold up. Real deployments want a realm per tenant, or different IdPs for different tenants entirely, one institution on Keycloak, another federating straight to Google. On the released versions, 1.14 and earlier, you cannot do that: the single instance-wide issuer is the wall you hit.
That wall, though, is coming down, and recently. An OIDC-federation feature has landed in Fineract's development line and is slated for the 1.15 release. It resolves the tenant from the token's own issuer, so each tenant can point at its own realm, or its own identity provider entirely, with configurable claim mapping and a default fallback for tooling. It is not in a numbered release yet, so if you are pinned to 1.14 or earlier the single-issuer limitation still applies and you should architect around it. But per-tenant identity is no longer aspirational; it exists in current Fineract. One detail to watch: the feature can optionally auto-create users on first login, which is contentious for a core banking system, so expect that piece gated or off by default, and keep pre-creating your users either way.
Entra and Google
The project documents Keycloak, and the steps for other providers are "similar," but similar hides real friction, and it all comes back to that sub claim. Microsoft Entra ID puts an opaque object identifier in sub; Google puts a long numeric account ID. Neither is a Fineract username, and unlike Keycloak you have less freedom to simply overwrite sub with something that matches. So while Entra and Google will issue perfectly good tokens that Fineract will happily validate, getting the subject to line up with your Fineract usernames takes more work, and there is no published, end-to-end guide for either. That gap is real; if you are wiring Fineract to Entra, you are charting it yourself.
There is also a grant-type wrinkle worth flagging. The documented example uses the password grant, where the client sends the username and password directly to get a token. That is convenient for a curl test, but it is not how you should run corporate SSO in production; real single sign-on uses the authorization-code flow with PKCE, where the user authenticates at the IdP and your front end never touches their password. The token Fineract validates is the same either way; the difference is entirely in how your client obtains it, and you want the proper flow in front of real users.
One last thing to keep separate in your head: Fineract has its own two-factor authentication feature, a one-time-code layer on top of basic auth. It is unrelated to SSO and to your IdP's MFA. If your identity provider already enforces multi-factor, that is where your MFA lives; do not confuse the two.
So, what should you do?
If you run a single Fineract for one institution, OAuth SSO is genuinely close at hand: flip the two switches, point the resource URL at your realm, add the Keycloak sub mapper, make sure your users exist in Fineract, and you are signing in through your corporate IdP. Keep your roles in Fineract, use the authorization-code flow for real users, and you have a clean setup.
If you host multiple institutions, the version you run is the whole story: on 1.14 and earlier the single-issuer limitation shapes your entire identity architecture, while current Fineract can now give each tenant its own issuer. Either way it is a decision worth making deliberately, not discovering at the wall.
That is the kind of thing we take off your plate. Finecko runs managed Apache Fineract with the identity integration set up properly, the right grant flow, the user mapping that does not 401, the roles governed inside the platform, and the multi-tenant identity design handled so each institution gets the isolation it expects. It rhymes with our security-hardening checklist for self-hosted Fineract: the pieces are all in the box, and fitting them together so they are secure and actually maintainable is the part that takes having done it before. If you would rather assemble it yourself, the map above is the one the docs never quite draw: a resource server, a token whose subject has to be your username, your roles staying put, and a single realm per instance on the older releases that current Fineract finally lets you split per tenant.
Skip the ops. Run managed Apache Fineract.
Finecko runs managed Apache Fineract for you - the Finecko Hub, the right topology, connection pooling, backups, TLS, patching, and on-call. You get the open-source core without the operations, and the free plan is a full environment to try with no credit card.