Every time a customer links a bank account, transfers money, or places a crypto trade, there’s a silent decision being made: Is this action allowed? Authorization answers the important question 'What actions are users allowed to perform?' This is especially crucial in the world of fintech, where there are no do-overs on getting it right.
Fintechs are one of the early adopters of tech in a country. If the largest bank in the UK starts using an innovative Identity Proofing technology, the rest of the organizations in the country are going to follow suit. It is a domain that builds on trust, money, and reputation incrementally over a long duration. This means the slightest mishap or failure to get it right can lead to loss of business, heavy fines, or complete blockade in a region due to non-compliance. And the competitors in the market are hawkishly on the lookout to dominate. The complexity in authorization is further compounded by the evolving challenges such as:
The days of embedding authorization logic deep within application code are far gone. Organizations now expect a more flexible, decoupled approach. This is where declarative authorization policies, supported by tools like Cerbos, prove invaluable. Remember Every authorization decision is ultimately a business decision.
In this article, we'll cover three representative fintech scenarios that closely demonstrate how to translate business requirements into authorization policies.
Before we discuss authorization policies, let's look at some of the factors we need to consider while designing them.
User experience (UX) and security should be rightly balanced. We don't want to be in a situation where unnecessary checks are put in place that do not improve the security posture. For example, if I am performing a funds transfer of $100, the app's SEND button need not perform a full round of compliance checks it performed 30 seconds ago on an earlier transaction on the same session on the same device.
Authorization policies need to be context-aware at all times, factoring in who the user is, what they’re trying to do, and under what conditions.
Segregation of Duties (SoD) must be maintained at all times. Especially in fintech, where you act as a custodian of users' money and digital financial assets. You have to be extremely clear on what roles can perform what actions on the platform in accordance with business and regulations. For instance the Customer Service Representative should not be single handedly allowed to resolve and approve a $250 refund request. A supervisor or finance department user should be responsible for approval.
Remember to aim for FAPI-compliant architectures that align with the security expectations of regulated financial systems, especially in scenarios such as Open Banking. This helps to be compliant and mitigate real-world attack conditions.
Now, let's take a look at scenarios that illustrate authorization policies at play.
Consider designing an authorization policy for an open banking consent flow. In this scenario, a user Bob logs into the finance app, MyFinApp, and begins the process of linking their personal bank account with StreetBank. The app redirects the user to StreetBank's site, where they log in and grant consent for which accounts they wish to share. Once consent is provided, MyFinApp enables Bob to view his bank accounts and balances, while also generating valuable insights into his spending habits.
At a high-level the requirement would be to make sure the user is eligible to provide consent. Once consent is granted the data shared with the 3rd party app should be inline with the user's consent, and the consent should be in an active state.
The core elements that form a part of the authorization policy are:
User
The end-user who is the account holder providing consent to share banking data.
Service provider
The third-party finance application that wants access to the user’s banking data for providing value-added services.
Authorization server
The system responsible for authenticating the user and managing the consent records.
request_consent
Triggered when MyFinApp initiates a request for access to the user’s banking data via open banking.
grant_consent
Performed by the user when they approve (or deny) access to their data on StreetBank's consent screen.
fetch_accounts
Used by MyFinApp to retrieve a list of bank accounts once access has been granted.
fetch_transactions
Used by MyFinApp to fetch transaction history for a selected account, within consented limits.
BankAccounts:{UserID}
Represents the user's account metadata (account types, numbers, etc.) retrieved after consent is granted.
Transactions:{AccountID}
Represents the transaction history for a given bank account, subject to time range and account scope constraints.
ConsentRecord:{UserID}
Represents the user’s consent metadata—status (e.g., active, revoked), scope, and expiry timestamp.
apiVersion: api.cerbos.dev/v1
resourcePolicy:
version: default
resource: ConsentRecord
rules:
- actions: ["request_consent"]
effect: EFFECT_ALLOW
condition:
match:
all:
- expr: request.principal.attributes.age >= 18
- expr: request.principal.attributes.email_verified == true
- expr: request.principal.attributes.account_status == "active"
NOTE
ABAC policy using user attributes like age, email status, and account status. Cerbos expressions on request.principal.attributes
enforce KYC-style checks before initiating consent.
apiVersion: api.cerbos.dev/v1
description: |-
Dynamic Open Banking Role to evaluate consent status
derivedRoles:
version: default
name: openbanking_roles
definitions:
- name: consented_user
parentRoles: ["user"]
condition:
match:
all:
- expr: request.resource.attributes.consentStatus == "active"
- expr: request.resource.attributes.consentExpiry > timestamp("now")
NOTE
Uses derivedRoles
to grant access if consent is active and not expired. Great use of dynamic conditions for real-time role assignment.
apiVersion: api.cerbos.dev/v1
resourcePolicy:
version: default
resource: Transactions
rules:
- actions: ["fetch_transactions"]
effect: EFFECT_ALLOW
condition:
match:
all:
- expr: request.resource.attributes.account_id in request.resource.attributes.allowed_accounts
- expr: resource.transaction_date > timestamp("now").subtract(request.resource.attributes.days_allowed, "days")
NOTE
ABAC policy restricts data access based on allowed accounts and days of history. Uses Cerbos time functions to enforce consent boundaries.
The open banking scenario is increasingly becoming prevalent where many e-commerce sites and retail applications are tapping in with financial institutions to provide customers holistic offerings. And as a result, there is going to be increasing concerns about how the authorization systems scale and how they can be designed in alignment with FAPI and evolving standards. It is, hence, important for an authorization system to be flexible and provide low-latency decision-making.
It's the most common scenario these days, all money transfers happen digitally within the country and across borders. Let's consider a simple scenario where a user Emma logs into a digital payments app FriendPay and wants to transfer USD 100 to John in Europe, to be received in Euros.
The requirement in this scenario would be to make sure all the pre-checks for transfer are satisfied for the user, origin country, and destination country, during transfer only allow the transfer if it can ensure that it was the user that initiated the transfer and that no fraud is taking place.
In this case, the core elements of the authorization policy would be:
Sender (Emma)
The user initiating the transfer of USD 100.
Recipient (John)
The receiving party who will get the funds in Euros.
friendsPay_app
The digital payments platform managing wallet balances and user sessions.
Payment processor
The backend service performing checks, currency conversion, and settlement.
initiate_transfer
Represents the start of the money transfer request by the sender.
validate_funds
Checks whether the sender has sufficient balance to complete the transfer.
process_payment
Executes the payment, including currency conversion and routing through the payment processor.
confirm_transfer
Finalizes the transaction and updates all relevant records to reflect the completed transfer.
Wallet:{SenderID}
Represents Emma’s wallet with available balance in source currency (USD).
Wallet:{RecipientID}
Represents John’s wallet, to receive funds in target currency (EUR).
TransactionRecord:{TxnID}
Ledger entry containing details of the transaction (amount, status, etc).
apiVersion: api.cerbos.dev/v1
resourcePolicy:
version: default
resource: Wallet
rules:
- actions: ["initiate_transfer"]
effect: EFFECT_ALLOW
condition:
match:
all:
- expr: request.principal.attributes.flagged == false
- expr: request.resource.attributes.destination_country in request.resource.attributes.country_whitelist
- expr: request.resource.attributes.origin_country in request.resource.attributes.country_whitelist
- expr: request.resource.attributes.transfer_amount <= request.resource.attributes.transfer_limit
- expr: request.resource.attributes.wallet_balance >= request.resource.attributes.transfer_amount
NOTE
ABAC policy validates user status, destination, limits, and wallet balance. Cerbos expressions make multi-condition rules easy to manage.
apiVersion: api.cerbos.dev/v1
resourcePolicy:
version: default
resource: TransactionRecord
rules:
- actions: ["process_payment"]
effect: EFFECT_ALLOW
condition:
match:
all:
- expr: request.resource.attributes.mfa_verified == true
- expr: request.resource.attributes.device_risk_score <= 0.3
- expr: request.resource.attributes.user_risk_score <= 0.5
NOTE
PBAC policy uses contextual risk signals like MFA and risk scores. Leverages Cerbos to integrate fraud checks directly into authorization.
According to a recent news article from ABC, Australia is set to become ‘functionally cashless’ by 2025. This means almost all transactions such as paying for goods and services and transfering funds to friends and family will be digital and as a result, the underlying systems must be highly reliable and accurate when it comes to authorization decisions.
With the global popularity of decentralized crypto-based currencies such as Bitcoin, Ethereum, Ripple and so on, lots of people now trade cryptocurrencies on 24-hour Crypto Exchanges. Let us take a look at how the authorization policies might apply for a user performing a high-stakes trade and when the user is withdrawing the funds to his offsite wallet.
Trader
A verified retail or institutional user attempting to trade on futures with leverage. A user initiating a withdrawal to an external wallet or bank account.
Crypto exchange engine
The system validating margin, leverage limits, and KYC flags before placing the order.
Crypto compliance engine
Background service evaluating EDD/AML flags and geolocation constraints.
leverage_trade
User attempts to place a leveraged futures position (e.g., 1:100 BTC/USDT).
withdraw_funds
User initiates a cross-border crypto withdrawal (e.g., USDT to EUR).
FuturesTrade:{TradeID}
A trading position resource that includes leverage, margin requirements, and collateral details.
WithdrawalRequest:{TransactionID}
The withdrawal request including amount, destination, risk indicators, and flags.
apiVersion: api.cerbos.dev/v1
resourcePolicy:
version: default
resource: FuturesTrade
rules:
- actions: ["leverage_trade"]
effect: EFFECT_ALLOW
condition:
match:
all:
- expr: request.principal.attributes.kyc_verified == true
- expr: request.principal.attributes.derivatives_eligible == true
- expr: request.principal.attributes.risk_score <= 0.5
- expr: request.resource.attributes.collateral >= request.resource.attributes.required_margin
- expr: request.resource.attributes.leverage <= request.principal.attributes.max_leverage
NOTE
ABAC policy validating based on user attributes (e.g., KYC verified, margin available) and resource attributes (e.g., leverage cap, exposure limits).
apiVersion: api.cerbos.dev/v1
resourcePolicy:
version: default
resource: WithdrawalRequest
rules:
- actions: ["withdraw_funds"]
effect: EFFECT_ALLOW
condition:
match:
all:
- expr: request.principal.attributes.edd_completed == true
- expr: request.resource.attributes.destination_country not in request.resource.attributes.sanctioned_countries
- expr: request.resource.attributes.amount <= request.principal.attributes.withdrawal_limit
- expr: request.resource.attributes.mfa_verified == true
- expr: request.resource.attributes.aml_flagged == false
NOTE
While access is primarily determined through attributes like KYC status, and region (ABAC), it also evaluates broader contextual business policies (e.g., risk score, AML checks, jurisdiction limits), making it partially PBAC.
Global crypto trading volume to surpass $108 trillion in 2024. And about half of the investment portfolios of Gen Z and Millennials constitute Crypto assets. This rapid growth means authorization systems must evolve to handle the volume and complexity of transactions in the crypto space.
Implementing authorization in fintech is a strategic shift - from embedding logic in code to managing policies as code. With Cerbos, you can define, test, deploy and manage fine-grained access controls that align with business rules, regulatory requirements, and user expectations.
The result? A scalable, secure, and compliant authorization system that adapts as your fintech platform evolves.
If you’re interested in implementing externalized authorization - try out Cerbos Hub for free, or book a call with a Cerbos engineer to see how our solution can help streamline access control in your fintech applications.
Book a free Policy Workshop to discuss your requirements and get your first policy written by the Cerbos team
Join thousands of developers | Features and updates | 1x per month | No spam, just goodies.