« All posts

Building auth on a store that only does get and put

How we built an authentication system on a store that only supports get and put, using key design and ETag-based concurrency instead of SQL.

An engineering team built a full authentication system, complete with users, roles, sessions, OAuth grants and refresh tokens, on top of Azure Table Storage, a key-value store offering only a partition key and row key with no joins, secondary indexes, increment operators or multi-row transactions. The choice was driven by cost and operational simplicity: no connection pools to manage, no database instances to scale, just per-partition scaling that comes for free, at the price of having no query planner.

The core technique is baking query logic into key design: since auth access patterns are known upfront, get a user by id, fetch a user's grants, consume a code, partition and row keys can be shaped to make every read a point-get. Instead of filtering on the store's server-managed timestamp, which forces a full table scan, the team built its own change-data-capture log, recording every write and delete into a separate indexed table so backups only touch rows that actually changed. Concurrency is handled without transactions, using ETag-based conditional writes and retry loops, a pattern that covers account lockout counters, single-use token redemption, and even leader election via atomic blob leases.

The trade-offs are real: no ad-hoc queries, no joins, and every invariant must fit inside a single item's atomicity. But for a domain like authentication, where access patterns are small, known and stable, giving up a query planner you'd never use in exchange for a nearly cost-free, failure-resistant storage layer is a deliberate and sound engineering bet, not a shortcut.