Service-to-Service JWT Trust Boundary
The auth service treats every request as untrusted until it proves it came from a known caller. A Fastify plugin installs one onRequest hook in front of the whole app, so the trust check runs before any route handler — there is a single choke point, not a per-route decision.
Two ways in. A request can present a service token in the Authorization header (Bearer scheme), or — for callers that never hold the shared secret — it can carry a session established by a signed-key handshake. Anything that satisfies neither is answered with a flat 401. Health endpoints are the deliberate exception: they are on an ignore list and pass without a token, and the plugin accepts extra routes on that list so specific ones can be opened on purpose.
Identity is the key that verifies, not the claim inside the token. The plugin holds a small registry of trusted callers, each bound to its own signing key. It tries the token against each key in turn; the caller's identity is decided by which key succeeds, and the token's own self-declared "who I am" field is never trusted for that decision. This is the load-bearing design choice — it makes the caller's identity cryptographically bound rather than self-asserted, so a token cannot lie about its origin. Some callers are additionally marked as trusted enough to skip the human-verification step at login, because their dedicated key is itself the proof of trust.
The token is a hand-rolled JWT over HMAC-SHA256. Signing and verification are built directly on the Node crypto primitives rather than a JWT library. Verification recomputes the signature over the header and payload, rejects any mismatch, and also insists the header declares the expected algorithm and type — so a token can't downgrade or swap its own algorithm. Expiry (exp) and issued-at (iat) are checked against the current time with a small bounded clock-skew allowance, so a few seconds of drift between machines doesn't cause spurious rejections while a genuinely stale or future-dated token is still refused.
Fail loud, not silent. The trusted-caller registry is assembled from configuration at startup, and every required key is mandatory — if any is missing, the service refuses to start rather than quietly running unable to authenticate that caller. A misconfigured deployment is a crash, not a silent hole.
What this boundary is not. It only answers "did this request come from a trusted service?" It does not extract user identity — that is a separate concern carried by session cookies and handled elsewhere. Keeping the two apart means the service-trust check stays simple and has exactly one job.
Related
- youteacher_auth — the auth service this boundary sits at the front of