Flow
The order my token check should follow
Each step only uses what the step before it has already checked.
1What mine did
Decoded the token and trusted the user and group.
2Check the token
Signature with the provider's keys, audience, expiry.
3Read the user
Only after the check passes.
4Require membership
Not in the group means no, not a default role.
5Outage is not no
A provider error should be a try-again.
6Author from the token
Never from the event payload.
Logged-out visitors now get turned away by the event collector. But the check that turns them away never asks who signed the login.
The collector gets events from my coding-agent hooks. I added a login check in front of it. It takes the token from the request, reads the user and the group out of it, and passes them on. When I read it back, I saw it only decodes the token. It never checks it.
Decoding just unpacks text. Anyone can write that text. Checking means testing the signature with the login provider's keys, then making sure the token is for my service and hasn't expired. My code skipped all of that.
Where the caller's own words ended up
Those unchecked values decide three things:
- whose account gets refreshed from the login provider,
- which group the request acts in, with a member role handed out even when there's no membership,
- whose name goes on each stored event.
A made-up user probably fails at the provider and gets turned away. A real user ID works, and those IDs aren't secret. So a fake token can borrow a real person, pick a group, and file events under their name. It can't create new memberships, because those come from the provider. When the membership lookup errors, the code just keeps going with the same default role.
A second change that day moved the provider's key into required startup settings. Good, but the decode didn't change.
The order it has to run in
const who = await verifyToken(token, providerKeys);
Check the token first. Only then read who it's for. If they aren't in the group, say no. Don't hand out a default role. Treat a provider outage as "try again", not as "no". Take the author of each event from the checked token, never from the payload.
When the collector answers with a 401, which means "not logged in", the command-line tool tells you to log in. It says your tools still work but nothing is saved until you do. That message is fine. The weak part is the server's 401, which a token with a real user ID in it still gets past.