jwt middleware in plain english
rexa moved 1,000+ daily transactions at 99.9% uptime — not because auth was clever, but because it ran before anything else could break.
rexa is a reward-exchange platform. users earn points, spend points, admins adjust balances. gamified, transactional, unforgiving if auth is lazy. we handled 500+ users and 1,000+ daily transactions at 99.9% uptime — and most of that reliability was middleware done early, tested often, and kept boring.
jwt in one sentence
login succeeds. server signs a token with user id and role. client sends it on every request. server verifies the signature before doing anything else.
no token → no handler. bad signature → 401. wrong role → 403. no "maybe guest mode" left on by accident.
auth isn't a feature you sprinkle inside handlers. it's a gate every request passes through once.
the pipeline
express middleware is functions in order. ours looked like this:
- parse json
- verify jwt
- attach user to
req - check role for this route
- business logic — only if steps 1–4 passed
app.use(express.json());
app.use(verifyJwt);
app.use(attachUser);
app.post('/rewards/redeem', requireRole('student'), redeem);
app.post('/admin/adjust', requireRole('admin'), adjust);what belongs in the token
sub— user idrole—student|adminiat/exp— issued at, expires at
what never belongs in the token
- passwords
- email or phone
- current point balance
- anything you'd hate to paste into jwt.io
rbac on a ledger
on rexa, a valid student token couldn't hit admin endpoints. the role lived in the payload, but we still re-fetched critical permissions from the database when money moved — tokens go stale. ledger operations shouldn't trust stale blindly.
mistakes i made early:
- putting too much in the jwt payload
- no refresh/expiry strategy — users logged in forever or got kicked at random
- catching auth errors inside handlers instead of centralized middleware
- returning
200with{ error: 'unauthorized' }instead of proper status codes
minimum viable auth
you don't need oauth on day one. you need:
- one clear login flow
- signed tokens with expiry
- middleware that rejects early
- a test proving a student can't hit
/admin/delete-user
ship that before debating auth providers. rexa ran for months on email + password + jwt before we even talked about social login.
draw the pipeline on paper. if auth is box seven inside your handler, move it to box two. your future self at 2am will agree.