Personal Finance AI Assistant: Read-Only Access and Privacy Isolation

    This guide explains how to design a personal finance AI assistant that can read balances and transactions without moving money. It covers OAuth read scopes, default-deny API gateway rules, field allowlists, session and tenant isolation, and the residual privacy risks that remain after those controls.

    1. Define the permission boundary first

    Before designing a personal finance assistant, the team should decide what the system must not do. A common approach is to request read access only: the ability to query account balances and transaction details. Write capabilities—transfers, payee changes, and orders to buy investment products—stay outside the authorization scope.

    An OAuth scope is the set of API categories an access token may call after the user grants consent. In the example used here, the access token includes only two read scopes: accounts.read and transactions.read. The token has no write scopes, its lifetime is short, and the user can revoke authorization at any time.

    Read-only access reduces one class of direct loss. Even if the model produces incorrect content, or a user message contains malicious instructions, the model has no write API it can call and cannot initiate a funds operation on its own.

    Read-only does not mean risk-free. Transaction history can reveal spending habits, income sources, and daily routines. Read-only permission lowers the risk of unauthorized money movement; information-disclosure risk needs separate controls. The two risks should be designed for separately and verified separately.

    2. Default deny at the gateway

    Permission control should not rely on authorization scopes alone. If the application has a vulnerability, a dependency is replaced, or model output is misused, an over-privileged request can still reach the backend. An allowlist on the API gateway can block those requests before they enter the business system.

    Use a default-deny policy: allow only reviewed read endpoints, and return 403 for every other path and method. Allowlist changes should go through code review, with the person who changed them and the reason recorded.

    The following configuration is illustrative. Field names and paths are examples and do not represent any specific product:

    1. {
    2. "_note": "Illustrative configuration, not a production config that can be run as-is. Secrets are not stored in this file; a secrets manager injects them.",
    3. "client_id": "finance-assistant-prod",
    4. "client_secret_ref": "vault://finance-assistant/client-secret",
    5. "oauth_scopes": ["accounts.read", "transactions.read"],
    6. "access_token_ttl_seconds": 900,
    7. "refresh_token_policy": {
    8. "rotate_on_use": true,
    9. "revocable_by_user": true
    10. },
    11. "gateway_policy": {
    12. "default": "deny",
    13. "allow": ["GET /v1/accounts", "GET /v1/transactions"],
    14. "deny": ["POST /v1/transfers", "PUT /v1/payees", "POST /v1/investments/orders"]
    15. },
    16. "logging": {
    17. "audit_enabled": true,
    18. "redact_fields": ["account_number", "id_number", "address", "phone", "token"]
    19. }
    20. }

    Tokens and the client secret are injected by a secrets manager. They are not written into config files or the code repository. Sensitive log fields are redacted before they are stored. After a user revokes authorization, the gateway should reject new requests that use that token. The team should verify this behavior in a test environment.

    3. Field allowlist

    Read-only authorization limits which APIs can be called. A field allowlist limits what those APIs return. A correct endpoint can still return an ID number, a full account number, or a home address. Those fields should not enter the model context.

    Apply the allowlist as follows:

    1. Enforce a field allowlist in the server-side serialization layer, and return only the fields required for analysis.
    2. Mask identifier fields such as account numbers, keeping only the last four digits.
    3. Do not return ID numbers, addresses, or phone numbers to the model, and do not write them to logs.
    4. Enforce the allowlist in code. Do not rely on a prompt that asks the model to be conservative.
    Field Handling Reason
    Amount and currency Keep Needed for spending categories and totals
    Transaction date Keep Needed for statement periods and trend analysis
    Merchant name Keep; truncate if too long Categorization depends on merchant information
    Account number Keep only the last four digits Enough to tell accounts apart
    ID number, address, phone number Do not return Not needed for finance analysis

    A field allowlist reduces two risks. The first is leakage of sensitive fields in API responses. The second is a model being induced, during a conversation, to repeat personal data it does not need. The limit is clear as well. Amounts, dates, and merchant names can still show spending habits. Results from many queries can still support inferences about a person even when every field is masked. If the allowlist is misconfigured, a new field can also reach the model without review.

    4. Session isolation

    Session isolation keeps one user’s financial data from being read by another user or another tenant. In practice, cover the following points.

    • Bind each session to a user. Store the user ID and tenant ID on the session, and check both values on every data query.
    • Do not cache across sessions. Cache keys must include the user ID and the session ID. Transaction details must not go into a shared cache.
    • Isolate tenants. Store each tenant’s data in its own storage namespace, and always include the tenant condition in queries.
    • Manage session lifetime. Set an idle timeout; the example value is 30 minutes. When the user logs out, revokes authorization, or the session times out, clear financial data from memory and cache.
    • Redact logs. Log the API path, time, user ID, and response status code. Do not write amounts, account numbers, or transaction details to ordinary logs.

    Residual risk remains. Within the same session, the model still holds financial data from earlier turns. A user may open the page on a shared device. Prompt injection can also request data that is inside the granted scope, so session isolation has to work together with the field allowlist.

    5. Control comparison

    Review each control against the risk it reduces and the residual risk that remains.