One OAuth integration became a platform
In 2024 UPS announced new OAuth 2.0 requirements for its API, and I was asked to build EasyPost’s side of it. The flow depends on a web browser, so the first version lived in our web backend.
Near the finish it was clear UPS wouldn’t be the last. FedEx, USPS and other carriers were heading the same way, and building each one inside the web backend would have meant more duplication and clutter.
So I designed a standalone identity service: a Python/Flask app that can act as both an OAuth client and an identity provider, with Redis holding user data during auth handshakes. The core is a class hierarchy, AuthClientBase → OAuthClient → UpsOAuthClient. The base classes handle the protocol, and a new integration mostly fills in what’s specific to that provider. The API contracts don’t assume a particular caller, so multiple EasyPost services use it.
I moved the UPS code over while building a new Amazon Shipping integration, and single sign-on requests followed quickly. By the time I left, the service handled:
- carrier OAuth for UPS, USPS, Amazon Shipping and TikTok Shipping
- SAML single sign-on for three customers, including Amazon
- MFA for EasyPost users through Twilio Verify
- session auth for EasyPost’s embedded and hosted UI products
I also shipped the matching SSO and MFA changes in the Rails Users service, including schema migrations on a table serving about 20,000 reads and writes per second.
Other teams could add integrations on their own, and the time to ship one dropped from about a month to about a week.