Back to Projects

Secure Banking System – FastAPI + PostgreSQL

A hardened banking backend with strict role-based access, hashed PIN authentication, immutable-style ledgers, and atomic money operations. Built to simulate real financial constraints such as account locking, fraud detection and concurrency safety.

FastAPI
PostgreSQL
JWT Auth
Audit Logs
RBAC
Atomic Transactions

System Architecture

Modular separation across auth, money operations, RBAC and audit-event logging.

Banking Architecture
  • Next.js client → FastAPI backend
  • JWT-based session and permission enforcement
  • Strict SQL transactions wrapping all money operations
  • Audit trail ensures accountability and traceability

Role-Based Access Control

Separation of duties between Customer, Teller, and Admin.

ROLE MATRIX

                    Customer   Teller   Admin
------------------------------------------------
View Balance         ✔️         ✔️        ✔️
Deposit/Withdraw     ✔️         ✔️        ✔️
Transfer Money       ✔️         ✔️        ✔️
Create Account       ❌         ✔️        ✔️
Lock/Unlock Account  ❌         ✔️        ✔️
View All Users       ❌         ❌        ✔️
View Audit Logs      ❌         ❌        ✔️

Permission checks occur on both the route layer and inside SQL procedures to prevent privilege escalation.

Database Schema

Highly normalised schema with ledger-style history tables.

users (id, user_name, password, role)
accounts (id, account_no, name, pin, balance, failed_attempts, is_locked, user_id)
history (id, account_id, amount, type, timestamp)
audit_logs (id, actor, action, details, ip, user_agent, timestamp)

PINs use bcrypt hashing and failed login attempts automatically lock the account. The history table works like a mini-ledger capturing all financial activity.

API Endpoints

Organised by feature domain.

AUTH
POST /register_account
PUT  /change_pin
POST /login

ACCOUNTS
GET  /get_account_by_account_no
GET  /get_accounts
GET  /get_all_potential_accounts

MONEY OPS
POST /deposit
POST /withdraw
POST /transfer_to_another_account

HISTORY + AUDIT
GET  /history/{account_no}
GET  /get_all_audit_logs  (admin only)
DELETE /delete_all_history (demo)
DELETE /delete_all_audit_logs (demo)

Request Lifecycle

Full validation pipeline.

  • JWT decoded → role + permissions verified
  • Account state validated (locked, failed attempts)
  • SELECT ... FOR UPDATE ensures row-level locking
  • Money operation + history entry wrapped in transaction
  • Audit log written

Concurrency Guarantees

BEGIN;
SELECT * FROM accounts WHERE id='A' FOR UPDATE;
SELECT * FROM accounts WHERE id='B' FOR UPDATE;
UPDATE accounts ...;
INSERT INTO history ...;
COMMIT;
  • FOR UPDATE ensures no concurrent withdrawal corrupts balance.
  • Strict serialisation of money movement.

Audit Logging

Forensics, fraud detection, and debugging.

  • Every action is traced with actor, IP, device, and timestamp.
  • Crucial for detecting teller misuse and fraud patterns.
  • Real banks use WORM storage; this demo allows clearing for testing.

Challenges & Solutions

  • Race conditions → fixed using SELECT FOR UPDATE + atomic commits.
  • PIN brute-force → automatic account lock after repeated failures.
  • Maintaining traceability → strict audit event generation.

Limitations

Honest constraints compared to real banking infra.

• No multi-factor authentication for high-value actions.

• No distributed transactions or cross-ledger coordination.

• Audit logs not append-only (demo constraint).

• No AML/KYC integration or fraud-detection engine.

What I Learned

✅ ACID transactions for financial safety

✅ Secure auth flows: bcrypt + JWT

✅ How banks protect ledgers and audit trails

✅ Structuring modular APIs with FastAPI routers