androidengineers.Book a session

Python 12 · HTTP and backend boundaries

Authentication, secrets, retries, and idempotency

articleSelf-paced

Learn the concept

Authentication establishes who is making a request; authorization determines what that identity may access. A token supplied by a client is not trustworthy until validated by the appropriate system. Do not invent a custom production authentication scheme for a learning project.

Load credentials from secure server configuration and scope them to the required service. Avoid printing tokens in logs or committing them to source control. For tests, use clearly synthetic identifiers and inject an already-validated actor into domain logic.

Retry only failures that may recover, within a bounded time budget. Mutations need an idempotency or reconciliation strategy because a timeout can occur after the remote action completes. An idempotency key is useful only if the receiving system stores and enforces its meaning.

Run and inspect

results = {}
def create_once(operation_id, value):
    if operation_id in results:
        if results[operation_id] != value: raise ValueError("Conflicting replay")
        return results[operation_id]
    results[operation_id] = value
    return value

assert create_once("op-1", "ticket") == "ticket"
assert create_once("op-1", "ticket") == "ticket"
assert len(results) == 1

Your exercise

Test a repeated operation ID with the same payload and with a different payload. Explain why the in-memory example is insufficient across restarts or multiple servers.

Check your understanding

Replays are deterministic, conflicting reuse is rejected, and the limitations of the teaching example are explicit.

Guided workshop: model an API before opening a socket

An HTTP boundary turns bytes into domain requests and domain results into status codes. First test the behavior as a function. This makes it possible to verify validation and authorization without relying on a server or a model provider.

# academy-check
import json

TOKENS = {"fixture-alice": "tenant-a"}

def handle(token, body):
    tenant = TOKENS.get(token)
    if tenant is None:
        return 401, {"error": "unauthorized"}
    try:
        data = json.loads(body)
    except (ValueError, TypeError):
        return 400, {"error": "invalid_json"}
    if not isinstance(data, dict) or not isinstance(data.get("question"), str):
        return 400, {"error": "question_required"}
    question = data["question"].strip()
    if not question or len(question) > 500:
        return 400, {"error": "invalid_question"}
    return 200, {"tenant": tenant, "question": question}

assert handle("fixture-alice", '{"question":" help "}')[1]["question"] == "help"
assert handle("unknown", '{}')[0] == 401
assert handle("fixture-alice", '[]')[0] == 400

These are deliberately fake tokens for an offline contract exercise. A production service resolves a verified identity through its authentication layer. Never let a caller choose their tenant merely by sending a tenant field in JSON. Secrets belong in server configuration, not browser JavaScript or committed fixtures.

Exercise: add a fake transport that returns a timeout on its first attempt and success on the second. Retry at most once for a read-only request. Then consider a ticket-update request that times out after the server commits it.

Worked answer: the second situation has an uncertain outcome. Repeating it safely needs a stable operation ID and server-side deduplication, or a query to reconcile the first attempt. Retrying every exception blindly can duplicate writes. Authentication failure and malformed input need correction rather than retries.

Checkpoint: describe a request's method, path, headers, body, status, and response body. Distinguish 401 authentication failure from a verified user's forbidden action. Define timeout and retry budgets together so repeated attempts cannot exceed the caller's overall deadline.

YOUR LEARNING JOURNEY

0 of 120 available lessons completed

Progress saved in this browser. No account needed.
Authentication, secrets, retries, and idempotency | Forward Deployed Engineer | Android Engineers