Add instruction 'how' to perform a REST API request by adding header or body param? - api

Imagine a simple REST API that allows to create a user account, by sending a JSON resource to POST /users as in the following. By default it sends out a confirmation email to the user.
{
"username": "john#appleseed.com",
"password": "secret"
}
However sometimes there are good reasons for not sending out a confirmation based on the use case, e.g. another API client, or admins signing up users on their behalf.
Since it doesn't have any implications on the created resource but is more of an instruction how to create the user, should it be separate from the request body? What's the best way to do this?
Specify a custom header Confirmation: no-confirmation
Add a query param ?confirmation=false
Add a send_confirmation field to the request body

Let's take the options in order:
Adding a header value to indicate some semantic difference should be generally avoided. The API should be "browseable", meaning it should be discoverable following links only.
Adding a query parameter is, from REST perspective completely equal to creating another URI. It does not really matter how you expose it, the point is that the client needs to follow some links from the previous "state" it was in. This is actually ok, as long as the links to these resources indicate the different semantics you described: like creating users by admin, users creating themselves, etc.
Also note, that the API should not necessarily expose whether a confirmation is sent. The API should expose the "purpose", the server then can decide whether the use-case warrants a confirmation email.
Putting a send_confirmation in the JSON representation itself. This is ok, if this is a functionality available for the user. For example I can ask for a confirmation email. If I can't, and it is only used for differentiating different use-cases, then I would rather prefer option 2.
Summary: For the case you are describing I would pick option 2: different resources for admins and normal users.

Related

Putting the calling user-id into the header of a REST-API

I am exposing REST API's via an API Gateway so the requests go
Client => API Gateway => Back-end.
My back-end REST API's need to know about 2 users
1: Target User (on some requests)
the API needs this data for its action eg: groups/{abc}/add-user needs to know which user to add to a group
2: Calling User (on every request):
the API needs to check that this user is allowed to perform the action eg: only a group admin may add another user to the group.
I don't want other developers to get confused when looking \ adding to my API's so I want to create a convention. Is it a good idea to add the calling user ID as a header on every request?
eg: "user-context": "12345"
Before everyone screams "INSECURE!!!!", I should add some info to put your minds at ease
The calling user is allowed to know their user-id. It's not a problem
The target user is not identified by their real user-ID but by an obfuscated ID
The calling-user-id is actually passed in a signed JWT to my API Gateway. The API-Gateway validates the JWT, takes the calling-user-id and adds it to the back-end request. What i'm really asking here is whether the API-Gateway should put the calling-user-id in the header (eg: "user context": "12345") or the query string /myapi/?calling-user-id=12345
I've tried both approaches and they both work. I'm wanting to know which approach makes the most sense to other developers.

REST API URI Design for sensitive resources

I have an application backed by RESTFul API. The application have user management section through which an admin user can manage other users. One sample URI for one of the API operation endpoint is below.
Update User : POST https://example.com/api/users/user1
Here user1 is the Username of the user being edited by the admin.
Suggestion from the security side is to remove the username from the URI since it is sensitive info and since it is part of url it will be recorded in network logs. Solution suggested is to pass the username data in POST Request Body .
Moving the data to request body is fine. But if I remove the username from URI ,the URI will be like "**POST https://example.com/api/users**" . This clearly doesn't look like a valid REST URI. And my USER entity doesn't have any other unique property which can be used in the URI.
Is there any recommended way to form a proper REST URI in such a scenario ?
POST /api/users
This clearly doesn't look like a valid REST URI.
Sure it does.
REST doesn't care what spelling you use for your resource identifiers, so long as the spelling is consistent with the production rules in RFC 3986.
That said, there's no particular reason that the identifier for a document needs to include sensitive information.
There are a couple of possible solutions - if the client and the server both know the sensitive data, then you can use a hashed value, rather than a raw value, as part of your identifier.
That's not ideal: we have mechanical ways of communicating URI that accept parameters, but no standard that I know of for communicating that some value should be hashed first.
If code-on-demand is an option, you might be able to manage to instruct the general purpose client to hash the data before sending it.
Otherwise, I think you are reduced to communicating the hashing out of band -- imagine a web form that instructs the human being to type in the hashed value of the sensitive information.
REST is optimized for the use cases that it was optimized for, and that means that some other use cases are more clumsy than we might like. Hooray for trade offs.
One way is to use an "id" instead of "username":
https://example.com/api/users/{id}
where "id" is usually a UUID https://en.wikipedia.org/wiki/Universally_unique_identifier

Ignore or not API endpoint parameters based on access level

I am working on an API endpoint that returns a list of products:
"api/products"
The endpoint accepts the following parameters:
page_size
page_number
Each product has a boolean property named IsApproved.
In the web application used by common users I always want to return only the Approved products ... On the web ADMIN application used by administrators I want to return all products, Approved or Not ...
My idea would be to add a new parameter (enumeration) named:
ApprovedStatus
And the values would be Approved, NotApproved and All.
On each API call I would check the user permissions ... If is admin I will consider the value on this parameter. If not then I will always return only approved products.
Another solution would be to have different endpoints ...
Any advice on which approach to take or is there other options?
The approval status is part of the product, therefore, in a perfect REST world, you don't want a different endpoint at all since you're accessing the same resource.
Then, for filtering a resource based on a property value, I think the convention is that if you specify that property as a query parameter it will only return those matching the value, and if not, it will return all of them, so I don't see the need to define a special ApprovedStatus parameter with some special values. Just query by isApproved!
Finally, about how to handle authorization. This, I think, should be handled at a completely separate layer**. If authorization is involved, you should have an explicit authorization layer that decides, for a specific resource and user, wether access is granted or not. This means the query would be triggered and if one of the resources generated by the query fails to be authorized for the user that triggered the query, it's taken out of the results. This accomplishes the behaviour you want without having any code that is checking specific users against specific query parameters, which is good because if tomorrow you have another endpoint that exposes this objects you won't have to implement the same authorization policy twice. Pundit is a perfect example on how to do this with Ruby elegantly.
**Of course, this approach retrieves data from the database unnecessarily which could matter to you, and also opens your endpoint up to timing attacks. Even then, I would consider tackling these problems premature optimizations and should be ignored unless you have a very good reason.
You're right about your ideas:
You can create a new endpoint just for admins, that will return all products
You can use a kind of authorization (e.g. Authorization Header) in order to check if the API is being called through admin or normal user. Then you can route internally to get all products or just IsApproved products.
You can add a proxy in front of your API to route to the right action, but it can also be achieved directly in the API but I think the second solution is easier.
Adding one more property is a bad idea.
In my opinion, adding another end point is very good. Because it will increase the protection in the admin end point.
Otherwise, since it is a web application, Simply set a cookie and a session to identify and separate the admin and user.
Going with the principle of least astonishment, I'd be in favour of adding a second endpoint for admin users. Such that you'll have:
GET /api/products (for regular users)
GET /api/admin/products (for admins)
This allows your code and API documentation to be nicely separated, and all of the admin-specific authentication details can live under the "admin" namespace.
The intention behind each API call is also clearer this way, which helps developers; and means that you can differentiate between admin vs regular usage in any usage stats that you track.
With ApprovedStatus, I think the specifics here don't matter much, but - considering what a developer using the API might reasonably expect / assume - it would be good to:
Ensure the ApprovalStatus parameter name matches the property name for "approval" that you return with each product object
Defaults to "approved" if it is not specified
Alert the user when an invalid value is specified, or one that they don't have access to
Bottom line: to answer your headline question - I think it's bad practice to ignore user input... sometimes. Design your API such that distinctions around when input can be passed in is very clear; and always alert the user if you receive input values that are technically acceptable, but not in the way that the user has requested, or for their access level. Ignoring values that are plain wrong (e.g. an argument that doesn't exist) is another story, and can be useful for future proofing or backwards compatibility.

Apigee App Services - Securing sensitive data in /Users collection

I've got a collection of users stored in the default App Services /Users collection.
Out of the box, to create a user, you only need to collect the following data:
{
"username" : "john.doe",
"email" : "john.doe#gmail.com",
"name" : "John Doe",
"password" : "test1234"
}
Name and email aren't exceptionally sensitive (albeit still should be properly secured), but if I wanted to include address, date of birth, mother's maiden name, etc., this data would become significantly more sensitive.
Luckily /users data isn't available without authenticating, but it is if I request an access_token and log in.
Of course one could easily design a front-end that obscures sensitive bits, hiding it from view. But looking at the underlying endpoint, it wouldn't take much to capture out my access_token and make an authenticated GET request to /{org}/{app}/users, thus seeing all of every user's personal information.
Is it possible, through roles and permissions or ownership, to limit segments of an entity only to self? (For example, the logged in user could access their entire user entity, but only limited segments of other user entities).
If not, is there a different way to approach this predicament and secure sensitive information in user objects?
Remus,
Unless you delete or modify the "Default" role, you cannot see one user's data with another user's token. So nothing to worry about there.
Also, as a matter of good practice, you should make sure you are sending the oauth token in the header, rather than in the query string (e.g. don't do ?access_token=''.
Rod
I would suggest that you put App Services behind API Services (Apigee Gateway) then rewrite the payload.
Basically (although it's a few extra steps than I'm going to be able to put into a short response) you would do the following:
1) Create a proxy in API Services to your usergrid.com target (https://api.usergrid.com/{yourorg}/{your app})
2) Create a /users resource.
3) Either use a Javascript callout to rewrite the payload with only the elements you want to expose or use the ExtractVariables policy to pull the specific elements you want to expose and the AssignMessage policy to Set a new Payload with only the response you want to expose.

RESTful way of having a single resource based on authentication

I have an API that provides an Account resource based on the authentication (login) that is supplied. As a user can only have one account, and can only see it's own account and not those of others, this API will basically be a single resource API in all cases.
So to keep things simple, I have this resource under the url accounts/ and when you access accounts/?username=dude&password=veryhard you'll get your account data (if you dohn't supply authentication you'll get a 403).
Now I wonder if this is RESTful. Also, you should be able to update your account info, and I wonder if PUT would be appropriate. In my knowledge, PUT should be done on a unique URI for the resource. Well, is this a unique URI for the resource? Generally a URI for an account would look like accounts/3515/ where 3515 is the account id. However, users don't know their account id. Also, there should be more ways to log in, instead of a username + password you should also be able to use a token (like accounts/?token=d3r90jfhda139hg). So then we got 2 URL's that point to the same resource, which also isn't really beautiful for a RESTful URI, is it?
So, what would be the most RESTful solution? Or should I not do this RESTful?
REST purists will consider that use of /accounts/ to obtain a single account is bad practice as it should specify a collection. Instead consider a key which cannot be mistaken for an ID, for example if your IDs are UUIDs then use a token such as 'me' so your URL is /accounts/me. This has the advantage that if later on you wish to obtain different account information, say for example you need to list users or you have an administration system using the same API, then you can expand it easily.
Putting username and password in the URL is also not pure REST. The query parameters should be directly related to the resource you are obtaining; commonly filtering and limiting the resources returned. Instead you should seriously consider using something like HTTP Basic authentication over an encrypted (HTTPS) connection so that you separate out your authentication/authorisation and resource systems. If you prefer to use a token system then take a look at oauth or hawk.
Finally, yes if you use PUT you should supply a full resource identifier. Given that it is very common for systems to read data before updating it the lack of ID won't be a problem as that will come back as part of the prior GET.
Yes accounts/?username=dude&password=veryhard is a correct REST URL.
PUT is used with an id if it used to update a resource, if you use it to create you must supply an ID. otherwise you use post to create a resource without id