18 April 202514 min read
Banking API Standards: Open Banking and Beyond
BankingAPI DesignOpen BankingSecurity
Implementing banking APIs that comply with Open Banking standards. PSD2 requirements, security considerations, and consent management.
Banking API Standards: Open Banking and Beyond
Open Banking transformed how banks expose APIs. From my experience implementing these systems at Lloyds Banking Group, here's a comprehensive guide to building compliant, secure banking APIs.
Regulatory Landscape
Understanding the regulatory framework is essential before writing any code.
PSD2 and Open Banking
| Regulation | Scope | Key Requirements |
|---|---|---|
| PSD2 | EU/UK | Access to accounts, SCA, TPP licensing |
| UK Open Banking | UK | Standardized APIs, CMA9 mandate |
| Berlin Group | EU | NextGenPSD2 API specification |
| FDX | US/Canada | Financial data exchange standard |
Third-Party Provider Types
TPP Categories:
├── AISP (Account Information Service Provider)
│ ├── Read-only account access
│ ├── Transaction history
│ └── Balance information
├── PISP (Payment Initiation Service Provider)
│ ├── Initiate payments on behalf of user
│ ├── Requires explicit consent
│ └── SCA mandatory
└── CBPII (Card-Based Payment Instrument Issuer)
├── Confirmation of funds
└── Card scheme integrationSecurity Architecture
Strong Customer Authentication (SCA)
SCA requires two of three authentication factors:
interface SCAFactors {
knowledge: 'password' | 'pin' | 'security_question';
possession: 'mobile_device' | 'hardware_token' | 'smart_card';
inherence: 'fingerprint' | 'face_id' | 'voice_recognition';
}
interface SCAExemptions {
// Transactions that may be exempt from SCA
lowValue: boolean; // Under £30 (limits apply)
recurringPayment: boolean; // Same amount, same payee
trustedBeneficiary: boolean; // Customer-whitelisted
merchantInitiated: boolean; // MIT with prior consent
corporatePayment: boolean; // B2B payments
}
function requiresSCA(transaction: Transaction): boolean {
// Low value exemption with cumulative limits
if (transaction.amount < 30 &&
transaction.cumulativeAmount < 100 &&
transaction.transactionCount < 5) {
return false;
}
// Recurring payment to same payee
if (transaction.isRecurring &&
transaction.payeeId === transaction.previousPayeeId &&
transaction.amount === transaction.previousAmount) {
return false;
}
// Default: SCA required
return true;
}OAuth 2.0 + FAPI Implementation
Financial-grade API (FAPI) extends OAuth for banking:
interface FAPIAuthRequest {
response_type: 'code id_token';
client_id: string;
redirect_uri: string;
scope: 'openid accounts' | 'openid payments';
state: string;
nonce: string;
code_challenge: string; // PKCE required
code_challenge_method: 'S256';
request: string; // Signed JWT with request details
}
// Creating a signed request object (JAR)
function createRequestObject(params: AuthParams): string {
const payload = {
iss: params.clientId,
aud: params.authorizationServerUrl,
response_type: 'code id_token',
client_id: params.clientId,
redirect_uri: params.redirectUri,
scope: params.scope,
state: generateSecureRandom(),
nonce: generateSecureRandom(),
exp: Math.floor(Date.now() / 1000) + 300,
iat: Math.floor(Date.now() / 1000),
claims: {
id_token: {
openbanking_intent_id: {
value: params.consentId,
essential: true
}
}
}
};
return signJWT(payload, privateKey, 'PS256');
}Certificate-Based Authentication (MTLS)
import https from 'https';
import fs from 'fs';
interface BankingClientConfig {
certificatePath: string;
privateKeyPath: string;
caCertPath: string;
baseUrl: string;
}
function createBankingClient(config: BankingClientConfig) {
const agent = new https.Agent({
cert: fs.readFileSync(config.certificatePath),
key: fs.readFileSync(config.privateKeyPath),
ca: fs.readFileSync(config.caCertPath),
rejectUnauthorized: true
});
return {
async request<T>(endpoint: string, options: RequestInit): Promise<T> {
const response = await fetch(\