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 7 of 19 · ~3 min

Other OAuth Grant Types

For almost any flow with a real human at the keyboard, Authorization Code plus PKCE is simply the correct choice — but the spec doesn't stop there. It lays out a handful of other grant types, each one built for a situation the code flow was never meant to cover. Tasklight genuinely needs two of them; its earliest prototype also leaned on two more that, in hindsight, it never should have touched.

Grant typeFitsStatus
Authorization Code (+ PKCE)A real person, a real browser — Tasklight Web, the CLIRecommended default
Client CredentialsMachine to machine, no human anywhere in the loopStandard, still current
Device CodeA screen with no comfortable way to type — Tasklight DashStandard, still current
ImplicitFormerly: SPAs with no backend, returning a token straight in the redirect URLDeprecated
Resource Owner Password CredentialsFormerly: a trusted first-party app collecting the password directlyDeprecated

Client Credentials, for the nightly digest worker. Once a night, a cron job needs to pull a usage number from billing-service and email it to each team's admin. There's no user session to attach this to — nobody is sitting at a keyboard — so the worker authenticates as itself, using its own client_id and client_secret, both held server-side and never exposed to anything.

digest-worker  ---- POST client_id="tasklight-digest-worker" + client_secret ----> Kestrel
digest-worker  <----------------------- access_token (scope: usage:read) ---------- Kestrel
digest-worker  ---- GET /usage, with that access_token ---------------------------> billing-service

Device Code, for Tasklight Dash. The office dashboard is a browser running full-screen on a TV, with no keyboard attached and no realistic way to type a password on it. Device Code flips the interaction around: the TV shows a short code, and Priya approves the login from her phone.

Dash          ---- requests a device code ------------------------------> Kestrel
Dash          <--- device_code (for itself) + user_code "QMKX-7291" ----- Kestrel
Dash displays: "Go to tasklight.dev/connect and enter QMKX-7291"
Priya, on her phone ---- opens that page, enters the code, approves ----> Kestrel
Dash          ---- polls the token endpoint until it's approved --------> Kestrel
Dash          <--------------------------- access_token ----------------- Kestrel

That polling step has a real gotcha worth naming: Kestrel's device-code response includes an interval value — five seconds, typically — and polling faster than that gets Dash a slow_down response instead of an early answer. It's a small detail, but it's the kind of thing that turns into a confusing bug report ("the TV takes forever to log in, but only sometimes") if whoever wrote the polling loop hardcoded a one-second retry instead of reading the value Kestrel actually sent back.

The two Tasklight doesn't use anymore. Back when Tasklight was three people building a prototype, two shortcuts felt reasonable and both turned out to be mistakes. The very first web login skipped the code exchange entirely and had Kestrel hand back an access token directly in the redirect URL — the Implicit flow — which meant the token sat in browser history and in the referrer header of the very next page it visited, for as long as that browser kept it. And an early command-line prototype, before the loopback-based PKCE flow existed, just asked developers to paste their Kestrel username and password straight into the CLI, which typed them into a token request itself — the Resource Owner Password Credentials grant. It worked, and it was exactly the thing OAuth exists to prevent: a client handling the actual password, indistinguishable from a phishing tool if the CLI itself were ever compromised. Both are gone from Tasklight now, but recognizing them matters — they still turn up in older codebases and outdated tutorials, and the reasons to avoid them are the two specific failure modes just described, not a vague sense that they're "old."