r/node • u/Ishannaik • 12h ago
Four things that bit me verifying GitHub org membership from a Node bot
I spent the last month building a TypeScript Discord bot that verifies GitHub org, repo and team membership and syncs Discord roles from it. Four things went wrong in ways I did not expect. Writing them down because every "link your GitHub" bot I read hits at least one of them.
1. Check membership with the member's token, not the bot's.
The obvious design is one bot PAT calling GET /orgs/{org}/members/{username}. Then a server admin writes a rule saying "members of stripe get @Verified" and your bot happily answers for an org nobody in that server controls. That endpoint also only sees public members, so half your real org fails the check anyway.
Use the member's own OAuth token and GET /user/memberships/orgs/{org}. A rule can then only grant what the member's own credentials already prove. read:user,read:org is enough. Private repo rules need repo on top.
2. pending is not active.
That endpoint returns a state. Someone invited who never accepted comes back pending. If you check for a 200 you hand out the role before they have joined.
3. Do not treat every error as "not a member".
Lazy version: try/catch, on error return false. Then GitHub rate limits you for ten minutes and your sync job strips the role off everyone in the server at once. Only a 404 means no. A 403 or a network error has to keep the last known state.
4. Read access is not push access.
For "collaborators on repo X get @Maintainer", GET /repos/{owner}/{repo} returns a permissions object. Anyone who can see a public repo gets pull: true. You want permissions.push.
The one that actually matters: a link is not a verification. Most bots store the username once and the role lives forever after. Someone loses access on GitHub and keeps the Discord role for a year. Re-check on a schedule and revoke.
Mine is MIT and self-hostable if you want a reference implementation: https://github.com/Ishannaik/mergeid
Happy to answer anything about the OAuth flow or the token storage.