Dylan Smith

Death by a thousand cuts

I committed an API key and all I got was this irreparable integration.

I fucked it this evening.

For five years, email.geeks.chat has been reliably pumping applications out to our Slack community’s admin team. It’s almost entirely unchanged in over half a decade.1

When someone submits the form, it hits a Slack webhook. The application details get formatted and sent into a private #admin-applications channel. If an application merits approval, we click “Approve” on the message. That makes an API call that sends a workspace invitation to the applicant’s email address. It’s a nice workflow but there’s nothing technically fancy about it.

Here’s where it gets silly. The application page predates my GitHub account. I FTP into the server. While all my more recent sites are hosted on Netlify, I have a few stragglers and have taken a “don’t fix it” approach if they’re not broken. Since creating my GitHub account, I have created repos for those sites so I can use version control, but never bothered to sync the repo with the hosting. It simply didn’t change often enough that the annoyance of firing up the FTP client was worth addressing.

Another problem: I created the repo for this site before GitHub offered private repos on its free plan. So what to do with that Slack webhook and API key? I decided I just wouldn’t commit that file. Security solved. Until tonight. I decided I would make some changes to the form handling but noticed the repo didn’t match what was on the server. Let me just sync those up real quick. Oh fuck. I just committed the secrets. And the repo is public.

It took under a minute to receive two emails from Slack: one for the API key and one for the webhook. They revoked both. Good on them for the instant detection and response. I felt dumb, deservingly, but luckily no harm done. I’ll just go make the repo private, sort out a better way to handle the secrets, re-generate them, and get the form back up and running.

Turns out the reliable API that’s been ticking away in the background for us for five years has been deprecated. Slack users no longer get issued API keys; everything is an “app” now. And the users.admin.write API method I was using was deprecated with it, replaced by admin.users.write.

Minor barrier. I create a Slack app. I generate the webhook URL. I add the new API method to the permissions scope. I… get an error. With the new API, admin permissions for apps and bots are only available on Slack enterprise plans. Slack’s live chat was as helpful as possible, in that they seemed very knowledgeable in telling me there’s nothing I can do. I can’t access the new API method. I can’t generate a new token for the old API. I’m SOL.

All this results in the admin team having to manually invite new group applicants instead of being able to click the nice little button. It’s not a big deal. We get at most a few dozen applications on a busy day and we split them up. I’ve spent more time writing this than the manual workflow will cost me. But it’s the principle of it, y’know?

I’m thinking about other ways we might be able to remove the friction and reduce the number of clicks in the invitation process. Without investigating at all, I’m wondering if I can do something with Zapier where we can trigger an email with one of Slack’s shareable invitation links. We’ll see.

I guess the lessons here are not to commit API keys (d’uh), not to mess with your Slack API tokens if you’re using the admin scope on a non-enterprise workspace, and maybe that sometimes you should fix it even if it ain’t broke.

  1. The applications used to land in Trello and trigger a Slack notification but have gone straight to the private Slack channel for the last few years. I changed that partly for convenience and partly because we didn’t want a Trello board full of applicant data sitting around. And I do mean full — we got so many applications that the amount of cards on the Trello board was causing awful performance issues. 

Newer It’s okay not to speak Older Never forget where you came from