Project 1: A secure orders API
Marking guide version 1 · Lessons: BE1 lessons 2 and 4, BE2 lessons 2–3, BE5 lessons 2–4, BE6 lessons 1–3, BE10 lesson 2
The problem and who it's for
TiffinBox's API works when everyone behaves. It reads tokens without checking their signature, shows any customer's orders if you change a number in the address, trusts the total the app sends, makes two orders when a phone retries, and lets one caller send thousands of requests. Meera orders lunch every weekday; she shouldn't be charged twice when her network drops, and nobody else should see her address.
Scope
- Verify access tokens against the practice sign-in server's keys, with issuer, audience, expiry and algorithm checks.
- Require scopes per route, and take the customer from the token, not the request.
- Validate new orders, price them on the server in paise, and answer errors as problem details without stack traces.
- Make
POST /v1/ordersidempotent with theIdempotency-Keyheader, safe for repeats sent at the same moment. - Replace the in-memory rate limit with a shared one in Redis that returns
429withRetry-After.
Not included
- A real identity provider or real payments.
- Account sign-up, password storage or an admin interface.
- Deployment to a public server (optional extra).
Before you start
- Core: Module 42 (web servers with Express), Module 44 (authentication), Module 47 (REST API design), Module 48 (API security).
- Path: BE1, BE5 and BE6 complete.
- The kit installed, with
npm run test:apirunning (11 of its 13 tests fail at first).
Starter files
tiffinbox/README.md: setup, run and test commands.tiffinbox/api/auth.js: decodes tokens without verifying them (deliberately unsafe).tiffinbox/api/orders.js: takes the customer from the request, trusts the client's total and has no idempotency.tiffinbox/api/rate-limit.js: counts requests in each process's memory.tiffinbox/db/schema.sql: the tables, includingrequest_keysfor idempotency.tiffinbox/tests/api.test.js: the 13 acceptance tests for this project.
Download everything at once: starter/backend-starter.zip.
Milestones
- Run
npm run test:apiand note which of the 13 tests fail and why. - Verify tokens in
api/auth.jswith the sign-in server's JWKS (tests 2 to 5). - Add scope checks and take the customer from the token (tests 6 and 7).
- Validate orders, price them on the server and return problem details (tests 8 to 10).
- Add idempotency keys with a unique constraint (test 11).
- Move the rate limit to Redis (test 12), then run all 13 three times in a row.
Expected outputs
- 13 of 13 API tests passing, three runs in a row.
- No route that reads a customer, price or role from the request without checking it.
- A short table: each route, the scope it needs, and how ownership is checked.
Acceptance tests
| # | Input | Expected result |
|---|---|---|
| 1 | npm run test:api |
13 of 13 pass, three runs in a row |
| 2 | A token signed with a different key, sent to GET /v1/me/orders |
401 (test 5) |
| 3 | Test customer 1's token with ?customer_id=2 |
Only customer 1's orders (test 7) |
| 4 | A valid order with "total_paise": 1 |
201, with the total computed from database prices (test 10) |
| 5 | The same Idempotency-Key sent 5 times at once |
Exactly one order; every reply refers to it (test 11) |
| 6 | 41 order-list requests in a minute with RATE_LIMIT=40 |
Request 41 gets 429 with Retry-After; another customer is unaffected (test 12) |
Failure and recovery exercises
- Change your token check to skip the audience. Run test 4 and explain what an attacker could do with a token meant for another API. Then put the check back.
- Remove the unique constraint's
ON CONFLICThandling and run test 11 a few times. Explain the race you see, then restore it. - Stop Redis and call
GET /v1/me/orders. Write two sentences on why the kit's rate limiter lets requests through (fails open) here, and name one route where you'd fail closed instead.
Evidence to submit
- Link to your repository or a shared folder with the kit and your changes: the deliverable link.
- The output of
npm run test:apishowing 13 passed, pasted into test evidence. - A recording of under 5 minutes showing a forged token refused, another customer's orders refused and a repeated order made once.
- Your explanation: how tokens are verified, where ownership is checked, and how repeats are made safe.
Marking guide and self-check
Pass rule: every Safety-critical (S) and Mandatory (M) item must be met. A single unmet safety item means the project is not complete, whatever else is done. Quality items are for your own improvement.
Your answers are saved in this browser on this device. Self-checked means you checked your own work against this guide; it is not a reviewer's approval. Never paste passwords, keys, tokens or real customer data here.
