Skip to main content
CodeOath
← All posts

Auth & Security63 min total · 19 parts

OAuth 2.0 and JWT Explained: What Really Happens When You Click "Sign In"

Part 3 of 19 · ~2 min

The Actors in an OAuth Flow

Four distinct roles show up in any OAuth flow, and pretty much every "wait, who's actually talking to whom" moment can be traced back to blurring two of them together without realizing it.

RoleWho this is, for Tasklight
Resource OwnerPriya — the person who owns the Kestrel account and can grant or refuse access to it
ClientTasklight — whichever of its five surfaces is asking for access on Priya's behalf
Authorization ServerKestrel's login and consent infrastructure — issues tokens once Priya approves
Resource ServerWhatever API actually accepts the token and hands back data

That last row is doing more work here than it looks like, because Tasklight's resource servers aren't all the same company as the authorization server. Kestrel is the Authorization Server for everything — it's the only party that ever sees Priya's password and the only party that ever issues a token. But once Tasklight has a token, two of its own backend services accept it: tasks-service, which owns projects and tasks, and billing-service, which owns subscriptions and usage. Both of them trust tokens Kestrel signed. Neither of them is Kestrel. That's a completely ordinary shape for a real system — one central identity provider, several unrelated APIs behind it — and it's worth holding onto, because it's exactly the setup that makes one particular mistake possible later in this reference: a token minted for one of those two services getting waved through by the other.

The confusion this table is really guarding against showed up on Tasklight's own support queue early on, in a ticket that read "Kestrel is down, nothing works." What was actually down was tasks-service — a deploy had gone wrong — while Kestrel's login page was working perfectly. The person filing the ticket had merged "the thing that logged me in" and "the thing that's now refusing to load my board" into one mental actor, because from where they were sitting, both failures looked identical: a spinner that never resolves. They're not the same system, they don't share an on-call rotation, and they don't even share a deploy pipeline — they just happen to agree on what a valid token looks like. Keeping the four roles distinct isn't pedantry; it's the difference between debugging the right service.