Securing server APIs for mobile apps without user registration
The Problem
Let’s suppose you have to create an API for your mobile app, and the app must be fully functional without asking the users to register (first). This could be for various reasons, for example:
- You’re just serving some content, and there is no obvious benefit for you or the users from registering
- You could turn down many users from using the app by asking them to register
- Creating the social login or login/registration workflow with emails is tedious
- You’re vary of GDPR policies and would rather not to have access to any personal data if you don’t need to
Yet, you still want to make your APIs exclusive to your app in order to prevent misuse of your data and the resources that you’re paying for.
There are a couple of options being used in the wild, but all of them are flawed:
- Fully open API - we already decided this is unacceptable
- Using a secret key shared between the app and the server - This is easy to break, as the apps are relatively easy to decompile and have all the string constants extracted from them. And if the key gets compromised, you’ll have to update both the app and the server, until the new key gets compromised just as the previous one
- Splitting the key to different parts of the app, or using some simplistic algorithm to generate one - This isn’t much harder to discover than a stored string, so it’s just as futile as the previous option
Now, even if you require users to register, someone savvy enough to extract their legit access token to your APIs can still misuse them if you don’t have a mechanism in place to limit their usage, such as request limits and throttling. You can limit your API usage based on the IP address (and you should always do that!), but this limitation can be easily avoided by using different proxy servers, or by having access to a fleet of hosts with different IP addresses.
The Solution
So, you still need some form of registration. But instead of users, you could register a device running your app. But how do you make sure that it’s your app that is asking to register, and not something else? With something unique to your app: push notifications.
Disclaimer: there are no bullet-proof solutions, and neither it’s this one. However, it’s good enough to keep those less tech-savvy and less motivated at bay, while enabling you to apply different security strategies just like for APIs with user authentication.
Without further ado, here is a full sequence diagram of the device registration workflow:
TL;DR: the app is asking the server to send it a registration URL via push data message, that is also encrypted with a public key generated by the app.
After the registration is complete, you can continue with the usual refresh token flow to access the APIs.
Some important notes about the challenges:
- Time to complete the challenge should be relatively short, no more than a couple of minutes
- Endpoints for requesting and completing the challenge are open, and should be heavily throttled
- There should be a scheduled job in place to regularly delete all of the expired challenges from the database (or set a TTL value if using something like Redis)
Generated device ID could be an UUID, or anything similar. And if delivering push notifications is a regular feature of the app, it’s a good idea to store the ID with the push service registration token together somewhere (effectively registering a device account) so you could easily update the token on the server when the app receives a new one from the push service. Note that you don’t need to ask users for permission to retreive data messages without displaying a notification.
Having the device ID in the access and refresh tokens will allow you to limit usage per device, and in case of a detected misbehavior, even deny refreshing the access token (thus shutting down the device account either temporary or permanently). Expiration time for the new access tokens should be reasonably short.
Usage in The Real World™
While I have a PoC project with this design, it’s not ready for production yet. But if you like the idea and decide to use it in production yourself, please do give me a shout on how it went over any channel where I’m present! Maybe there are no comments on this website, but I’d be happy to publish any real world experiences (with your permission) as a part of this article.