Skip to main content

From native sign-in to revocable financial access

· 6 min read
Ireti Engineering
Product and platform engineering

A successful login is not the same as permission to read a financial account. We tested that distinction in Phenix Lite: a native Android application signed in through Ireti IAM, displayed an authorized own-balance response, and then denied the next read after access was revoked, without requiring the user to sign in again.

This was a bounded development proof on an Android emulator, not a production launch. Its value was demonstrating the complete journey and the boundaries that must remain intact when we extend it.

Three things we set out to prove​

The first outcome was native authentication. The app needed to open the Android system browser, authenticate through Ireti IAM backed by Keycloak, and return to the native screen. A web demonstration would not establish that the Android callback worked.

The second outcome was an authorized financial read through the intended architecture:

Phenix Lite -> APISIX -> private BFF -> APISIX Financial API -> private Phenix/Fineract.

The third outcome was current-state revocation. Once an administrator removed the application's delegated permission, the next read in the existing mobile session had to fail and the app had to remove the previously displayed balance.

We verified all three.

1. Native sign-in without a password flow in the app​

Phenix Lite uses Flutter AppAuth, backed by Android's native AppAuth SDK, rather than implementing an OAuth protocol or collecting an IAM password inside the application.

The native registration is a public client. It has no client secret and uses Authorization Code with S256 PKCE. Its exact callback returns the authorization response to Android. Implicit and password grants are disabled.

PKCE protects the authorization-code exchange; it does not turn the native application into a confidential client. Service credentials belong on the server, not in the APK.

For this slice, the mobile token and displayed balance remain transient in memory. Closing the local session clears them. That local action does not claim to terminate the separate IAM browser session.

2. Permission and ownership are separate checks​

There are three decisions that are easy to confuse:

  • Identity consent: the user allows the application to receive identity information through IAM.
  • Delegated API permission: Ireti Platform authorizes the application to act for the human within the reviewed resource scope.
  • Account ownership or binding: the product decides which account that human may access.

An identity claim does not establish account ownership. An administrator granting delegated API permission does not establish ownership either.

Our BFF validates the user token and exact native client, asks Ireti Platform for a current delegated decision on every read, and enforces an explicit product-owned demo account binding. The caller cannot supply an account selector.

The downstream financial hop has another boundary: a separate confidential service registration needs its own Application permission. That service token is not a substitute for the user's delegated authorization.

APISIX provides the reviewed ingress and fixed upstream boundary. It does not become the source of permission truth. Ireti Platform owns the current permission decision; the product owns its account records and enforcement.

The native BFF had no host-published port. Mobile requests used APISIX rather than bypassing it through direct BFF ingress.

3. Revocation without waiting for token expiry​

A signed token can remain valid after a grant is revoked. If a service relies only on token contents, permission may remain usable until that token expires.

We intentionally tested a stronger outcome: the user removed the native application's delegated permission, kept the mobile session open, and requested the balance again without signing in or restarting.

The observed native result was denial with the mobile session retained and the formatted balance absent. This demonstrated both server enforcement and the user-facing clearing behavior.

Clearing the old value matters. An app that rejects a new read but continues displaying an earlier balance gives the user an ambiguous result.

Evidence, not a production claim​

The development acceptance included:

  • an actual debug APK built, installed and running on Android;
  • system-browser authentication returning to the native signed-in screen;
  • an actual own-balance read succeeding after administrator consent;
  • a subsequent read denied after revocation in the same mobile session;
  • eighteen focused mobile tests, focused analysis and the Android build passing;
  • BFF tests, vet and build checks;
  • gateway routing, selector-denial and bounded rollback checks.

What this gives us​

The proof gives us a working pattern for extending Phenix Lite without mixing identity, platform permission, financial-domain authority and gateway routing.

Authentication starts the session. Current permission authorizes each operation. The product establishes which account is available. A separately authorized server performs the financial read.

The most important result is not simply that a balance appeared. It is that the same application stopped displaying it when access was removed.