Skip to content

OIDC can be configured but never used — the login page offers only username and password #370

Description

@Aman-Mittal

Summary

system/oidc-config writes a complete OpenID Connect configuration to Fineract. The login page
has no idea it exists. An administrator fills in an issuer, a client ID, a client secret, the
authorization and token endpoints and a JWKS URL, ticks enabled, and the sign-in form still
offers exactly one option: a username and a password, sent as Basic auth.

The configuration is real and it is persisted. Nothing consumes it.

Evidence

The screen is a faithful editor for /v1/tenants/{tenantId}/oidc-config — all four generated
operations exist and it uses three of them:

@endpoint get    /v1/tenants/{tenantId}/oidc-config
@endpoint post   /v1/tenants/{tenantId}/oidc-config
@endpoint put    /v1/tenants/{tenantId}/oidc-config
@endpoint delete /v1/tenants/{tenantId}/oidc-config

It writes issuer, clientId, clientSecret, authorizationEndpoint, tokenEndpoint,
jwksUrl and enabled (oidc-config.component.ts:36-42).

The application contains no OIDC runtime at all:

$ grep -rli "angular-oauth2-oidc|oauth2|authorization_code|id_token" src/app --include=*.ts | grep -v src/app/api
(nothing)
$ grep -n "oauth|oidc" package.json
(no dependency)

AuthService.login() posts a username and password to /v1/authentication and stores
base64EncodedAuthenticationKey for Basic auth. That is the only path in.

Business Value

  • Single sign-on is usually not optional. An institution running a core banking back office
    generally has an identity provider, and a requirement that staff authenticate through it —
    centralised revocation, password policy, session control, audit. Without it this application
    keeps a second, independent set of credentials outside that control, which is precisely what
    such a policy exists to prevent.
  • A configuration screen that does nothing is a false assurance. An administrator who fills
    this in has every reason to believe SSO is now in force. It is not. That belief is worse than
    knowing the feature is absent, because nobody goes looking for what they think they already have.
  • Basic auth means the password is replayed on every request. Credentials sit in
    sessionStorage and are attached to each call. Moving to a token flow removes a standing
    credential from the browser, which is worth having on its own terms.

Scope

  • Login offers an identity-provider route when the tenant has OIDC enabled, and falls back to
    username and password when it does not. The fallback matters: a deployment without OIDC must be
    untouched.
  • Authorization-code flow with PKCE — the client secret in the tenant config is for the backend,
    not for a browser, and must not be shipped to it.
  • The resulting token replaces Basic auth on outbound requests, which means
    auth.interceptor.ts gains a second mode rather than a second interceptor.
  • Token refresh, and a sign-out that ends the provider session rather than only the local one.
  • A failed or cancelled provider round-trip must land somewhere legible, not on a blank page.
  • Decide and document what happens when OIDC is enabled but a user still has local credentials.

Verify this first

I could not establish what Fineract does to Basic auth once a tenant has OIDC enabled, because
configuring it needs an identity provider the e2e stack does not run. On the current stack the
tenant has no configuration at all:

GET /v1/tenants/default/oidc-config → 404 "No OIDC configuration found for tenant: default"

So the first task is to find out whether enabling OIDC also disables password authentication.
If it does, this issue has the same shape as #369 — a switch that takes the deployment offline —
and its severity is much higher than "a screen that does nothing". Please answer that before
designing the flow, and record the answer here.

Tests — required, not optional

Unit. auth.service.spec.ts and the interceptor: a session established by token rather than by
Basic auth; the token attached to outbound requests and the Basic credential absent; refresh; the
Basic-auth path unchanged when OIDC is off; sign-out clearing both.

E2E, mocked. A new spec beside e2e/rbac-route-protection.spec.ts. This is where most of the
coverage belongs, because the provider can be mocked at the network boundary the way config.json
and /v1/authentication already are: the identity-provider button appears only when the tenant has
OIDC enabled; a successful round-trip lands on the dashboard; a cancelled one returns to login with
a reason; a rejected token does not create a session; with OIDC off the existing username and
password flow is byte-for-byte unchanged
— that last case is the regression guard for every
current deployment.

E2E, real provider. One spec against a real identity provider added to the e2e compose stack,
proving the authorization-code round-trip end to end. It needs its own Playwright project and must
stay out of the default run, like the 2FA project in #369, because it needs a container the normal
suite should not require.

RBAC. The permission set arrives in the authentication response today. Whatever it arrives in
under OIDC, AuthService.hasPermission() must still be the only place permissions are evaluated,
and check-route-permissions must still pass. A token flow that quietly bypasses the route guard
would undo #368.

Documentation

  • DOCS/ — the flow, what is configured where (tenant config in Fineract versus anything the
    browser needs), why PKCE and why the client secret never reaches the browser, how to run a local
    identity provider, and how to run the real-provider spec.
  • security.md — the trust boundaries change when a token replaces a replayed password. The
    threat model section should say so, and the caller-roles table needs revisiting.
  • DOCS/RBAC.md — a note on where permissions come from under OIDC, so the answer is not left to
    be re-derived.
  • A deployment note covering tenants that already have OIDC configured and have been running on
    Basic auth without noticing.

Related

#369 — two-factor authentication has the same shape: a configuration screen the login flow does not
honour. That one is already confirmed to break the application outright, so it should probably go
first.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions