I'm working on a system using an Express.js API and a frontend website where users log in with their credentials to interact with the API. My company wants to allow paying customers to use the API directly through tools like Postman or their own applications. However, I want to ensure that regular users cannot directly access the API unless they are paying customers. Currently, users authenticate via an endpoint that provides a JSON Web Token (JWT), which works for both the frontend and Postman. What strategies can I implement to prevent non-paying users from accessing the API directly while still allowing paying customers to do so? I'm looking for a solution that avoids completely refactoring our current API implementation.
5 Answers
The main issue seems to be that you're mixing authentication with authorization. Your JWT currently verifies identity but doesn't address access levels. The simple fix is to include a tier claim in the JWT when issuing it. Regular users get a token with `tier: "web"`, while paying customers get one marked as `tier: "api"`. Then, you just add middleware to check this tier for protected routes. Even if a frontend user copies their JWT from the network tools, it won't grant them the right permissions. Also, consider making those tokens short-lived, around 15 minutes, to reduce any potential misuse.
Do short-lived tokens actually help? Users could just script their browser to refresh the token every 15 minutes.
Let’s be real, no method is completely secure against someone determined to bypass restrictions, but that doesn't mean we shouldn’t try to make it as inconvenient as possible. Here are a few ideas: Require a valid HTTP referer, implement CAPTCHA on login, and automatically log users out after inactivity. It's more about making it less appealing for users to exploit your setup.
All these complex answers overlook the simplest solution: create separate API routes or apply varying rate limits based on user payment status. It's essentially about putting in place some mechanisms to control access, making it more attractive for users to pay for API access rather than trying to game the system. You can even consider heavy pagination or rate limiting to reduce their ability to scrape freely.
For an added layer of security, you might consider setting access limitations based on the `req.headers.referer`. This way, only requests coming from your specified frontend URL get through when using a specific API key, although keep in mind savvy users might spoof headers. Ultimately, while you can't fully restrict access, you can design a model that incentivizes legitimate use.
The issue remains that outside of a browser, anyone can adjust headers. Isn’t this a bit flimsy?
Absolutely! Switch to HttpOnly cookies for your frontend authentication and issue separate API keys for paying customers. The frontend can set an HttpOnly, Secure, SameSite=Strict cookie, which the browser sends automatically but is protected from being read by JavaScript. Meanwhile, API customers receive a Bearer token after signing up. Middleware on your server can check for either type of authorization without changing your API routes. Sure, some determined users might still find workarounds, but you'll make it a lot more inconvenient for them.

But how do you prevent a web tier token from functioning in Postman? If someone gets the token from their browser, how can your backend distinguish the request's origin?