Projects / FinReview - Portfolio intelligence platform for Indian investors / Architecture Decision
Choosing appropriate security over maximum security
Security engineering is often discussed as though more is always better. In practice, every security mechanism has a cost, and the appropriate level of security is determined by the threat model, not by what the most demanding production system would require.
When I designed authentication for FinReview, I considered four approaches.
The first was no authentication — routes would accept a user ID in the request body and return that user's data. This was briefly considered and immediately rejected. Any user with a valid user ID integer could read or modify any other user's data. That is not a trade-off; it is a failure of basic design.
The second was full JWT with RS256 signing, refresh token rotation, and a token revocation store. This is the approach used by production financial applications with real users and real money. It requires a dependency (PyJWT or python-jose), a refresh token storage mechanism, a revocation list, and careful handling of token rotation edge cases. It is also the approach most likely to contain subtle implementation errors when implemented by a single developer without peer review of the security-critical path.
The third was session cookies. These are simple to implement and well-understood, but they complicate CORS significantly for a static frontend served from a different origin than the API. Every protected request would require CORS credential handling that adds configuration complexity on both sides.
The fourth was a lightweight HMAC-signed token using Python's standard library only. The token format is frv1:{user_id}:{utc_timestamp}:{hmac_sha256_signature}. It is issued at login, verified on every protected route, and expires after 7 days. hmac.compare_digest is used for timing-safe signature comparison. No external dependency. No refresh token. No revocation.
I chose the fourth approach, and the reasoning was explicit: the threat model does not justify the complexity of full JWT.
FinReview in production holds no real financial accounts. A user registers with an email and password, adds transactions manually, and views analytics. There is no connection to a broker, no API key with trading permissions, no real money at stake. The risk of a stolen token being used maliciously is real but limited in consequence. The risk of a subtle implementation error in a custom refresh token rotation mechanism is also real, and potentially more damaging — a bug in the auth flow could lock out all users or expose all accounts.
The principle at work here is matching mechanism to threat model.
This is something I encountered repeatedly in 13 years of enterprise platforms. Systems that implemented maximum security for every component regardless of actual risk ended up with authentication flows so complex that developers worked around them, or with so much security infrastructure that the actual sensitive components were indistinguishable from the mundane ones. Security debt accumulated not from ignoring security but from applying the wrong level of it uniformly.
The HMAC token approach has known limitations, and they are documented. SECURITY.md states explicitly that the recommended next step for a production deployment with real users is full JWT with rotation and a revocation store. The current implementation is not presented as production-grade financial security. It is presented as appropriate authentication for the project's actual scope.
One late finding reinforced this: the admin shutdown endpoint was initially unauthenticated. An external review identified that an unauthenticated shutdown route in a hosted deployment could be triggered anonymously. The fix was not to implement a full privilege system — it was to add a single bearer token check matching the deployment secret. Simple, proportionate, documented.
Security is not about implementing every possible mechanism. It is about implementing the right mechanism for the problem being solved, being honest about its limitations, and documenting the path to stronger security when the threat model changes.