Authorize narrowly and retry without duplicating effects
How to study this lesson
Read one section, trace the worked example, and try the knowledge check. Bookmark saves a shortcut; notes appear in My notebook. Mark lesson complete is your own assessment and does not mark its lab passed.
By the end, you can
- Keep authentication, authorization, and user intent distinct.
- Design tenant-scoped idempotency keys with payload conflicts.
- Explain the atomicity requirements behind safe retries.
Possession of a tool is not a grant
Authentication establishes an identity or credential context. Authorization decides whether that context may perform this operation on this object. User intent supplies the requested task and may impose a narrower boundary than the credential's technical power. An assistant that can discover reserve_stock should not infer that every discovered operation is authorized for every tenant. Tool discovery, argument validation, and policy enforcement serve different purposes.
A useful policy decision combines principal, action, object, and context. For example, a service account may reserve inventory only for tenant north, only for approved SKUs, and only while its grant is valid. Keep this decision on the server even if the client also checks it. A changed prompt must not be able to widen a server-enforced permission.
Minimize what credentials can do
Least privilege reduces the reach of mistakes and compromised components. Prefer a read scope for stock lookup and a distinct write scope for reservation. Bind credentials to their intended resource, protect them in transit and storage, and avoid treating a token for one service as permission at another. RFC 9700 provides the primary OAuth security guidance behind this separation; the Python example below does not implement OAuth or cryptographic token validation.
Application scoping remains necessary after a token passes validation. A token with inventory:reserve may still be limited to one organization. Fetching an object by its globally unique identifier and forgetting the tenant predicate is a common design error. Apply tenant constraints before returning data and before committing a mutation, rather than filtering only the displayed result.
One intention can have several attempts
Idempotency means repeated execution has the intended same effect as one execution under the operation's contract. HTTP defines idempotent method semantics, but a business operation carried in a POST usually needs an explicit application design. A request ID alone does not provide that design. Associate a stable operation key with a canonical payload fingerprint and a recorded outcome.
Suppose inventory begins at ten. Reserving three units with key K leaves seven. Repeating K with the same payload returns the recorded result and leaves seven. Reusing K to reserve five units must produce a conflict, not return an unrelated success. Namespace K by tenant and operation so two customers choosing the same key cannot collide or see each other's outcomes.
The atomic boundary is the hard part
A dictionary demonstration hides the most important production requirement: the effect and the idempotency record need a coordinated durable boundary. If the process commits stock and crashes before recording K, replay may reserve twice. If it records success before changing stock, a crash can preserve a false success. A database transaction can cover both when they share a database; external effects require additional designs such as provider idempotency and reconciliation.
Concurrent attempts also need a unique constraint or equivalent serialization. Checking whether K exists and then inserting it in separate unlocked steps is unsafe. Record pending work explicitly when completion can outlive the request. Define how callers inspect pending or unknown outcomes, and how long completed keys remain available. Expiry can make a much later replay a fresh operation.
Retry policy follows evidence
Retry transient delivery failures with bounded attempts and an overall deadline when the operation supports a safe replay or reconciliation path. Do not repeatedly retry invalid arguments or insufficient permissions. Check authorization on every replay before returning a cached outcome; otherwise a revoked caller could retrieve old private results. A cached success is still protected data.
The demo performs a sequential in-memory reservation and returns copies of cached results. That prevents a caller from mutating the cached response through an alias, but it does not supply durability or concurrency safety. The lab expands the same mechanism with stock, scope, payload, and duplicate checks. Its portfolio value comes from explaining those boundaries and designing a production transaction, not from labeling a dictionary an exactly-once distributed system.
Work through the code
A sequential simulation starts with ten units and replays one reservation. The scope and input checks are intentionally supplied in the lab rather than this short mechanism demo. The shared dictionary is neither durable nor safe for concurrent production requests.
import json
stock = {"A": 10}
ledger = {}
def reserve(tenant, key, sku, quantity):
identity = (tenant, key)
fingerprint = json.dumps([sku, quantity], separators=(",", ":"))
if identity in ledger:
saved, result = ledger[identity]
if saved != fingerprint:
raise ValueError("idempotency conflict")
return dict(result)
if stock.get(sku, 0) < quantity:
raise ValueError("insufficient stock")
stock[sku] -= quantity
result = {"remaining": stock[sku]}
ledger[identity] = (fingerprint, result)
return dict(result)
print(reserve("north", "K", "A", 3))
print(reserve("north", "K", "A", 3))
print(stock)
{'remaining': 7}
{'remaining': 7}
{'A': 7}Run Python snippets locally with the prerequisites named above. The module coding lab runs directly in your browser.
Pause and reason
A server stores its idempotency record after calling an external billing service. Describe the crash window and a concrete repair.
Check your understanding
A caller retries the same operation key with a different quantity. What is the safest documented behavior?
Your notes
Explain the mechanism in your own words. Add a failure you want to test.