I'm kinda surprised I don't see this in more REST discussions, but I'm debating how to best provide a list of things to a user, based on who the user is, or what they have authorization to see.
For example, let's say that we have an API for a bookstore. I might have a resource URL of /books which would list all books. But if my application logic is such that some books are viewable to users who are anonymous, but others are only viewable to those who are logged in, I'm a bit more uncertain the best way to go.
Obviously, programmatically I could filter based on the identity of the user (gleaned from their API key or whatever creds I'm using) but I feel like that's a bit 'off' from the standpoint of REST design.
Feels more right to have two endpoints; let's say one for /books/public and one for /books/restricted and we can say that the latter returns a 401 if the user isn't logged in. Easy enough.
But that pattern breaks a bit if the books in question are just a list of books that the user has read or has bought or have some other relation to the user. I've seen some API's that would do something like /my/books in that case, but that (again) feels off since the /my/books isn't a unique URL per se, it changes based (again) on a header value (API key, etc).
This leaves me to think that maybe the "best" approach is to do something like /books/users/1235 or /users/1234/books to get the books that 'belong' to user 1234, and then return a 401 if someone not authenticated tries to hit that URL, or a 403 if they're authenticated but not authorized to view that resource.
I guess that's a lot of background to my main question: What's the best practice for REST API URL design when the resource data is dependent on user identity?
Rest is not a standard so there is technically no right or wrong way to accomplish what you're after. Having said that, I'll give you my opinion.
I propose you simply use /books unless there is a very specific need for a service or user to see which are unique to them and those that are public. In which case I suggest doing all three. The fact that there could be more or less content at the /books endpoint based on the user's privileges doesn't alter the intent, it enhances it.
But that pattern breaks a bit if the books in question are just a list of books that the user has read or has bought or have some other relation to the user. I've seen some API's that would do something like /my/books in that case, but that (again) feels off since the /my/books isn't a unique URL per se, it changes based (again) on a header value (API key, etc).
I believe providing a /my/books endpoint is ideal if the content is intended to be private. If your reading list IS private and specific to your account then having an endpoint without that is masked significantly dramatically reduces the risk of having user-identifying information leak via the url.
In the event that reading lists can be shared then you would want there to be a a discrete action. If a user wishes to share their reading list or a subset therein they would compose the list and a new endpoint would be created specifically for it like /book-lists/xxxx-xxxx-xxxx. If your user's reading list is public by nature then /my/books would simply redirect to /booklists/xxxx-xxxx-xxxx
This leaves me to think that maybe the "best" approach is to do something like /books/users/1235 or /users/1234/books to get the books that 'belong' to user 1234, and then return a 401 if someone not authenticated tries to hit that URL, or a 403 if they're authenticated but not authorized to view that resource.
I'm not sure if you are referring to the reading list or the first concept of filtered results by some form of credentials.
/books/users/1234 would suggest a correlation to users from the book's perspective. This would be more applicable for authors or publishers, people that are actually affiliated to books in some deeper connection other than your examples above elude to. The same logical connection, for me at least, applies to /users/3493/books but to a lesser extent.
I think as a general approach, you should scratch both of these out if you have any desire for the public links to be shared. You are potentially making a very convoluted structure that could ultimately result in people getting really confused why their friend can't see a list of novels because she sent her search results with a url had been "personalized" and thus secured.
Obviously, programmatically I could filter based on the identity of
the user (gleaned from their API key or whatever creds I'm using) but
I feel like that's a bit 'off' from the standpoint of REST design.
No, it is not off, because the credentials are part of every request and the response can be dependent on request parameters...
The url depends on whether you want to share personal data with others:
if you want to share the url
/users/{userId}/favouriteBooks/
/books/favouriteOf:{userId}/
if you don't want to share the url
/books/favourite/
Keep in mind that you always can change the url structure as long as your service applies the HATEOAS principle...
Related
When an authenticated user wishes to access a resource which he exclusively owns, it seems redundant to specify the user id in the URL path.
Thus, in the following examples, which is the more appropriate way to name my API endpoint?
Example 1
User wants to change profile pic
PUT /users/{id}/profile-pic
or
PUT /profile/profile-pic
Example 2
User wants to add a hobby to his profile
POST /users/{id}/hobbies
or
POST /profile/hobbies
It's a joy to see people paying attention to their API design in terms of URI and responses. An API that is not well designed is going to quickly die since people will avoid using them.
Even if it's not going to be public and no one will use it aside from yourself or your team, think of your colleagues and your future self and take some time to think about how your URIs will look like.
Back to your question my friend, according to the hands-on restful API design patterns and best practices book, that I invite you to read,the REST API is composed of four unique archetypes, as
follows:
Document: The document is the base for a resource representation with a field and link-based structure.
https://api-test.lufthansa.com/v1/profiles
https://api-test.lufthansa.com/v1/profiles/customers
Collection: A collection is also a resource, and it is a directory of resources managed by the API providers or servers.
https://api-test.lufthansa.com/v1/profiles/customers/accountbalance
https://api-test.lufthansa.com/v1/profiles/customers/memberstatus
Stores: A store is a resource repository managed by the client. The store allows the API client to put resources in, choose URIs for the resources that get added, get them out, and delete them when it decides.
http://api.example.com/cart-management/users/{id}/carts
http://api.example.com/song-management/users/{id}/playlists
Controller: Controller resources are similar to executable methods, with parameters and return values. REST API relies on controller resources to perform application-specific actions that do not come under any of the CRUD methods.
POST /alerts/245245/resend
So, in your case, you can follow the API design of GitHub API. Look how they are retrieving the projects of an organisation. Yours would look this way:
PUT /users/{id}/profile-pic
POST /users/{id}/hobbies
I'm sorry for making it long, I wanted to base my perspective on something concrete.
When an authenticated user wishes to access a resource which he exclusively owns, it seems redundant to specify the user id in the URL path.
It shouldn't; the semantics of a resource identifier and the semantics of an Authorization header are different.
The fact that only Bob can get a copy of /profile/Bob is a matter of access policy, not message semantics.
Review Fielding's definition of resource. "Bob's profile" and "Alice's profile" are distinct nameable information (assuming for the moment that Bob and Alice are themselves distinct) and therefore should have different identifiers.
That's the "RESTful" answer.
In practice, HTTP has special rules about authentication, and the handling of authenticated requests means that you'll probably "get away with" treating the Authorization header as part of the identifier of the resource (particularly in the case where an authorized user is only allowed to access their own resource hierarchy).
During registration I am asking for username, email, and password. I am checking if the username and email is unique via the following end points
HEAD /auth/:email
HEAD /users/:username
Uniqueness of emails and usernames is split into auth and users resource because I thought when logging in I am asking for email so that should be in the auth and checking if the username is unique or not can go in users.
Login happens at the following endpoint
POST /auth/login
A new user is created at the following endpoint
POST /users
This is a stateless API and I am wondering if my routes look RESTful?
I am wondering if my routes look RESTful?
REST doesn't care what spelling conventions you use for your resource identifiers, so long as they conform to the production rules described in RFC 3986.
See, for instance: Tilkov 2014.
Instead, you should choose your identifier spelling conventions to make things easier for your human beings: what spellings are easy to document? what spellings are easy to recognize in the access logs? and so on.
You might be better off with a single resource that reports the status of the email address and username together, rather than using two different reports to do that.
Similarly, using HEAD requests is a bit weird; you are apparently using metadata in the transfer of documents over a network domain to communicate your business semantics, which (a) is what the documents are for and (b) is an example of "accidental complexity" -- I won't go so far as to guarantee problems down the road, but you are certainly inviting them, and it's not obvious that the current benefits outweigh that risk.
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.
I am new to REST APIs, I am actually not looking for any workarounds, I just need to clear my concept because I know I am missing some very important information which can easily solve my problem.
For E.g I made a small website, let's say using angular.js, where I will list some information or some items.
Consider it as an open website for now, everyone is getting same list. Anyone can come to the page and see the list of items. Simple.
Lets say the URL to this list is
https://www.example.com/api/list/v1
Result returned
1. abc
2. cde
3. efg
4. hij
Perfect!
Now my site got traffic, and I decided to implement user thing on my website, so that every user can only see his own information.
So I changed the rest api url to this:
https://www.example.com/api/list/v1/12345
where 12345 is userid.
The user with the user id came on the website, logged in, and browsed the list, by chance he went to the source of the page, and he found the url to list information.
He copied the url and started to use different user id's, he might get lucky any time.
I am not talking about implementing any type of security up til now, there must be several ways to secure it.
My main concern is that isn't it a large security hole, or we can call it an invitation, for injections?
So I just want to clear my understanding, where I am wrong?
Thanks
This is not some security loophole. This is the way it is supposed to work.
Let's put aside the case of APIs. Consider the simple example::
I have a url: www.example.com/jobs/
This will list all the jobs on my website. No authentication or permission of any sort is required to access this data. I am providing it to every user that visits my website. Now I need a way to figure out if the user visiting my website wants to filter the jobs available on my website. So I provide this option in url kwargs. This will look something like::
www.example.com/jobs/java/
So, now the list will contain the data only for Java jobs. Plain and simple.
Now some day I decide that I will let only the registered users to view Java Jobs. So, I introduce a new check in my View that lets you access the java jobs only if you are logged into my website. Otherwise it redirects you to the login page. Depending on the requirement, you put restrictions on the data being sent to the user.
Same is the case with APIs. If you allow the data to be available to any user that uses the API, there is a flaw with your design and not with the concept of APIs. APIs are just the implementation of your logic.
If you don't want a user with id 12345 to access the data of a user with id 123, you have got to restrict the permissions on the API code. You have got to handle themselves. The API will respond to your code.
Hope this clears out everything.
I'm designing a hypermedia API, yes, a RESTful API, with the hypertext constraint.
Each user of the system will access the system using their own credentials, so every request we process is authenticated and authorized. Each user will typically have specific credentials so that they may have different permissione (e.g. none, read, read/write) on each collection.
We want the client to be primed with the one URI that it starts with, which would be perhaps an atom services document, or a hierarchy (draft atom hierarchy extensions) of atom collections.
My question is basically should users see different representations for the same URI, or should users be directed to different URIs based on their permissions?
For example: User A and User B have different permissions in the system. They log in with different credentials, to the same start URI. A successful response may be one of the following 2:
200 OK, and User A sees something different than user B on the same URI
302 (or other redirect) each user to e.g. /endpoint/userA (which they own)
The tradeoff between cacheability is of course minimal, since resources are cached only by the client and not by intermediaries, but there's a tradeoff for visibility too (URI contains (aythenticated) user ID). Finally there's the future possibility of allowing User A (or a super user) to see what User B sees.
I'm not asking what Twitter or Facebook do, I'm more interested in what REST practicioners have to say about this.
My question is basically should users see different representations
for the same URI, or should users be directed to different URIs based
on their permissions?
For example: User A and User B have different permissions in the
system. They log in with different credentials, to the same start URI.
A successful response may be one of the following 2:
200 OK, and User A sees something different than user B on the same
URI
302 (or other redirect) each user to e.g. /endpoint/userA (which
they own)
Both ways are RESTful. The representation of a resource can depend on the permissions. The communication is stateless because you send the credentials (username, password) with http auth by every request. Redirection to another representation variant after permission check is a great idea. That way you can completely separate the authorization logic from the resource representation logic, so you can move it even to another server and you can create very well cacheable resource representations. For example by GET /endpoint/userA you can redirect userA to /endpoint/userA?owner=true, because she is the owner of the profile, or you can create a composition of features: /endpoint/userA?feature1=true&feature2=false etc... It is very easy to setup fine grained access control for that. Another way to stay cacheable if you append the user id to every request's queryString, but this solution with redirection is much cleaner. Thank you for that!
Personally I find this a really tough call to make and I think it depends a lot how much content would change. If the difference is the omission of a few extra details then I would probably treat it as a single resource that varies based on the user.
However, once the differences start to get more significant then I would look at creating different resources. I would still try and avoid creating resources that are specific to a particular user. Maybe for a particular resource you could create a set of subresources, with differing levels of content. e.g.
/Customer/123?accesslevel=low
/Customer/123?accesslevel=medium
/Customer/123?accesslevel=high
This method in combination with the 302 may be sufficient in some cases. For more complex cases you could use multiple query string parameters.
/Employee/123?SocialSecurityNo=yes&SalaryInfo=yes
I do not believe there is an easy answer to this question. I think the answer is similar to most tricky REST scenarios: your solution can be as creative as you want as long as you don't violate the constraints :-)
Option 1, replying with 200 is an acceptable REST response and is more user friendly than option 2.
The Google Data APIs provide a decent REST implementation on top of their user services, and they implement option 1. For example the Google Calendar Data API allows a user to query the person's own feed by performing a HTTP GET request on http://www.google.com/calendar/feeds/default/private/full.