Auth & Security63 min total · 19 parts
OAuth 2.0 and JWT Explained: What Really Happens When You Click "Sign In"
Part 6 of 19 · ~2 min
PKCE: Authorization Code Flow for Public Clients
The CLI is where that problem actually lands. There's no server standing behind tasklight login — it's a binary sitting on a developer's own laptop, talking to Kestrel directly, with nowhere private at all to tuck a client_secret even if it wanted to. That makes it a public client, and left as-is, it would drag back the exact risk the confidential-client requirement was meant to close off: grab the authorization code in transit, and there's no secret standing between an attacker and a token.
The mechanism that solves it goes by PKCE — Proof Key for Code Exchange, spoken out loud as "pixie" rather than spelled out letter by letter — and it never once asks the CLI to guard a secret it has nowhere safe to put. The trick is to make the CLI prove it's talking to itself, across two separate requests, using a value it generates fresh and never actually transmits until the very end:
1. tasklight login generates a random string, kept only in memory: the code_verifier
2. it computes code_challenge = BASE64URL(SHA256(code_verifier))
3. it sends code_challenge — never the verifier itself — with the initial authorization request
4. Kestrel stores code_challenge, tied to whatever authorization code it eventually issues
5. when tasklight login exchanges that code for a token, it sends the original code_verifier
6. Kestrel hashes the verifier it just received and checks it against the stored challenge —
proof this request came from the same client that started the flow, without a static,
reusable secret ever existing anywhere
Concretely, one real run of tasklight login generates:
code_verifier: tsklt-cli_QPon63ijtRrVvpW_u1Sfes3tcowg16qEkgyILCtkxpk
code_challenge: XQ0tcQDLtZmE5Z-zbXwzpYhOn0dnU4ZCmBdtuF8kXQQ
code_challenge is what goes out over the network with the authorization request; code_verifier sits in the CLI's own memory until the token exchange, and never leaves the machine before that. Say somebody does manage to grab the authorization code off the CLI's local redirect handler — it briefly listens on http://127.0.0.1:8817/callback to catch it — redeeming it gets them nowhere: all that ever crossed the network was a one-way hash, and there's no computing the verifier back out of a hash once it's been produced.
Tasklight's web app runs PKCE too, on top of its client_secret, even though nothing forces it to — a confidential client loses nothing by adding it, so current OAuth guidance has stopped treating it as a public-client patch and started treating it as something every client should just do, secret or no secret.