Lion Cipher Lab
Enforce API authorization at the gateway (validate the JWT before it reaches your service)
- AppSec / DevSecOps
- Intermediate
- ~60 min
- Free · no account required
Why it matters
The moment you have more than one backend service, a hard question appears: where does 'is this caller allowed in?' get enforced? If every service reimplements token validation, you get five slightly different, slightly wrong implementations — one forgets to check `exp`, one forgets `aud`, one accepts `alg: none` — and rotating a key means editing five codebases. The industry answer is to centralize the coarse check at a gateway or reverse proxy: one consistent choke point that authenticates the caller, validates the token's signature and claims, rate-limits, and logs — once, in front of…
What you'll learn
- standing up a three-service API stack locally with Docker Compose — an unprotected backend, an NGINX gateway in front of it, and a small JWT-validating auth service
- using NGINX's auth_request directive to call an external auth service as a subrequest on EVERY request, so the gateway forwards to the backend only when that subrequest returns 2xx
- validating a JWT the right way with PyJWT — signature, exp, aud, and iss — and pinning the algorithm so a token claiming alg:none or a swapped algorithm is rejected
- watching the three outcomes with your own eyes: no token -> 401 at the edge, a valid signed JWT -> backend JSON returned, a tampered or expired token -> 401 — with the backend never seeing the bad requests
- putting the backend on a private Docker network with NO published host port, and proving nobody can bypass the gateway to reach it directly
- reasoning about WHEN a gateway is the right layer (a consistent choke point for authN, key rotation, rate limits, and logging) versus what MUST stay in the service (per-object / BOLA authorization), and why the gateway is defence in depth, not the only check
- spotting the classic gateway mistakes — accepting alg:none, skipping the aud/iss check, no key rotation, trusting client-supplied identity headers (the confused deputy), and leaving the backend port publicly reachable
- mapping the exercise to the OWASP API Security Top 10 (API2:2023 Broken Authentication) and knowing exactly which risk a gateway does NOT fix (API1:2023 Broken Object Level Authorization)
Steps in this lab
- The idea: one choke point instead of five copies of the same check
- Lay out the project and write the backend (deliberately unprotected)
- Write the auth service: validate the JWT the RIGHT way with PyJWT
- Write the gateway config: auth_request calls the auth service on every request
- Wire it together with Compose — and keep the backend OFF the public network
- Prove the three outcomes: no token -> 401, valid -> backend, tampered/expired -> 401
- Prove nobody can bypass the gateway (the private-network check)
- The judgment: what the gateway fixes, what it never will, and the common mistakes