Flow
Cases my install handoff has no tests for
1Webhook first
The row should still end up with exactly one stored token.
2Browser first
The path that works, and probably the one a quick click-through hits.
3Either one twice
The second arrival should change nothing.
4Someone else's number
A real number from another customer should be refused and write nothing.
Notes said the app checked who owned an installation. The code only checked that the installation was real. Those are not the same check.
My admin app has many tenants. A tenant connects a code host by installing our app there. After the install, two things come back. The user's browser lands on a setup page with an installation number. The code host's server also sends a signed webhook about the same install. Both need to end up on the same tenant.
The old code just counted tenants and picked the only one. With a second tenant, it broke. So I rewrote it with an agent. Now a logged-in request makes a pending row for your tenant and gives your browser a random value in a cookie. When the browser comes back, the app uses that cookie to find your row and writes the real number into it. The webhook now checks its signature, which it didn't before. Then it finds the row and stores an access token.
Two things are wrong with it.
First, the ownership check. The app asks the code host about the number using the app's own login. That login can see every install of the app, for every customer. So any real number passes. The number comes from the browser, and the link step never checks who is logged in. From reading the code, someone could start their own install and submit another customer's number. If that customer hasn't linked yet, it would stick. Real ownership proof has to come from the user, for example by having them authorize during the install and checking the number is in their own list.
Second, arrival order. The code itself says the webhook often gets there first. At that point the row still holds the cookie value, not the real number, so the webhook finds nothing and replies with an accepted status and a note that it will be retried. As far as I know, the code host won't send it again after that reply. The browser then links the row and waits for the webhook to finish the job. It never does. The app says connected anyway, and there's no token behind it.
If you're building the same kind of handoff, these are the cases I'd want covered:
- the webhook arrives before the browser
- the browser arrives before the webhook
- either one arrives twice
- someone submits a number that isn't theirs
For order, the simplest answer is one setup step that either side can run, guarded so it only runs once.
The notes that came with the change also promised a signed state value and rate limiting on the link step. Neither is in the link code. There are no tests either. The first one I'd write is the webhook showing up early, since that's the order the code seems to expect most.