This is a question about a best practice for securing a Web API endpoint.
Say, I am working for a bank that lets users use a mobile app. I am a developer working on the mobile app for that bank. The app gets OAuth access token and access a web API hosted by the bank. The app has been released.
A regular user who has a valid account in the bank installs the mobile app. Through the mobile app, the user can view the balance, transfer money, etc.
This user has a dev skill and noticed that he can get the access token after he signs in to the bank with his user name and password.
Because this user has a valid account for the bank, the access token is valid to call the API endpoints. The user does trial and error in his Visual Studio to figure out what requests need to be sent to get a valid response from the API. He can refresh access token as many times as needed with the official mobile app manually and eventually finds a way to make valid calls against the API from his dev tool.
Question is, are there any mechanisms that can be utilized to prevent the user from calling the endpoint without going through the official mobile app? The web API can be marked with [RequiredScope] attribute, for example, but if he was able to sign in, should he have all the permissions to do what the normal users are allowed to do, such as transferring money?
I have done searches on this topic on the web as it seems to be a common topic, but have not found references yet.
CodePudding user response:
When developing a UI client you should always assume that the user can grab tokens from a mobile app or secure cookies from a web app, then replay them against the API. Both of which are just HTTP headers. This is easy for any semi-technical user to do with an HTTP proxy tool.
SCOPES
APIs should validate the JWT access token received on every request. Then use scopes to prevent access to invalid operations. Eg if an access token without a money_write
scope was used to attempt that operation it would fail with a 403 error.
CLAIMS
The main protection is usually always done when the API's logic verifies claims received in the JWT access token. This ensures that the user can never elevate their own privileges. Consider the following example:
sub: hd80423r2f
tenant-id: 123
role: user
subscription-level: silver
If an API receives this, then it would typically apply code to deny access to resources for other users, roles, tenants or subscription levels. In these cases I most commonly return a 404 resource not found for user
response.
HIGH PRIVILEGE OPERATIONS
Operations such as money transfer would usually be accompanied by strong authentication with multiple factors, and short lived access tokens. It is common to also involve user consent, perhaps to a particular payment transaction.
Sometimes you need dynamic authorization behavior in APIs, eg only allow a money transfer if strong authentication was used. In these cases claims are used rather than fixed scopes, and this might result in the following extra claims in the JWT access token:
authentication_strength: high
payment_transaction_id: 123
The API's code could then deny access unless runtime claims such as these were also present.
SUMMARY
You cannot authorize based on use of a mobile app, since APIs cannot distinguish between HTTP requests from mobile apps or sent manually. The main thing to ensure is that if a user grabs a token using tools, they can only access the exact same data that they see in their UI user session.