Let's say for a CRUD app, there's Students and Tasks tables, with a one to many relation between them.
There's a frontend that allows a student to "log in", and a form to create tasks.
I always see an autoincremented numeric id being used as the unique identifier for the backend tables in REST API guides.
How does a user "log in" then? (assuming no authentication, this is just for a high level understanding on how the backend finds the resource from info the frontend provides). The frontend don't know the numeric autoincremented "id" nor does the user themselves, we must send something else to the backend, like username that the user inputted from the frontend right? To me, a GET route to /users/{userName} makes more logical sense than /users/{numeric_id}, if someone can give some guidance on if this thinking is correct.
Then when we create a task, we send whatever from the frontend is required for a task to save in the db (like taskName, for example), along with the userName of the person logged in, so the backend knows to associate that task to a user right?
Related
I have an API design/versioning conundrum.
Let's say I have an endpoint /api/customers which GETs all customers (ignore pagination). There's a twist though: if a regular user accesses this endpoint, they will only get the customers created by that user and no one else (I can check the access token and the sub field to determine who sent the request). Other usecase: if an admin accesses this endpoint, they should get ALL customers, regardless of who acquired them.
Now my question is from an API design perspective: is it better to have an if/else role check within the API controller itself to determine do I return ALL (admin) customers or specific (user) customers, OR should I differentiate between endpoints for the user and admin? I.e. admin only endpoint for all customers would be /api/admin/customers and regular users can still access their /api/customers?
In REST, it is normal to have multiple resources that share the same representations.
For example, the "authors' preferred version" of an academic paper is a mapping whose value changes over time, whereas a mapping to "the paper published in the proceedings of conference X" is static. These are two distinct resources, even if they both map to the same value at some point in time. The distinction is necessary so that both resources can be identified and referenced independently. A similar example from software engineering is the separate identification of a version-controlled source code file when referring to the "latest revision", "revision number 1.2.7", or "revision included with the Orange release." -- Fielding, 2000
It is perfectly consistent with that approach that you might have one resource for "all users", and another resource for "users created by Bob".
Where things get twisty is the case where you want to use the same resource identifier to provide different representations. That is, when Alice looks at "users created by me", she sees "users created by Alice", and when Bob looks at "users created by me", he sees "users created by Bob".
One possibility is to have "users created by me" redirect to the appropriate resource. It works, for values of "works" that permit extra round trips when the destination resource isn't already in the local cache.
In HTTP/2, server push may spare you some of that round trip pain.
The rules for shared caches should protect you from sending Alice's view of the "me" resource to Bob, and vice versa, but it is useful to be aware of the meanings of the various headers so that you don't inadvertently disable that protection.
Having different resources can be a problem in some "read your own writes" settings, because the caches won't know that an unsafe request has invalidated both resources. Bob creates a new user via a POST to "users created by me", and the corresponding cache entry is invalidated... but "all users" is a different cache key, and does not get invalidated. So if Bob looks at the all users view, he may see a previously cached copy without the changes that he just saw in his own view.
In some cases, it can make sense to consider sub-resources.
/api/customers
/api/customers#created-by-Alice
/api/customers#created-by-Bob
But if you are trying to reduce the amount of irrelevant data being exchanged, then that's not a good fit.
It should be same endpoint. Otherwise, each front-end which calling your API must have the same logic to determine the role and endpoint mapping.
It depends on your project.
If there's only 2 cases as you mentioned
only get customers created by that user for regular users
get all customers for admin users
then, it'd be better to use 1 endpoint by adding middleware to check current user role.
If you're plan to extend your project.
e.g. if admin users are also needed to get the customers created by that user, it'd better to create 2 endpoint. one for all customers, another one for current user's customers. like - api/customers/all, api/customers/me
I think /api/customers is fine for the cases mentioned. It's analogous to a web page request to index.html returning different content to different users.
If you want to extend it (e.g. Alice requesting Bob's list), you could support optional query params:
/api/customers?accessibleTo=bob
/api/customers?createdBy=bob
This would likely require an authorization check (Does Alice have access to Bob's list?), returning 403 (or 404, depending on your philosophy) when not authorized.
Also don't forget about caching. Avoid the possibility that two requests to the same URL (/api/customers) for different users will result in one user getting the other's list.
I'm building a web app with VueJS as front end and authenticating user to an API with JWT.
If I want a API to display only user products (filtered), should the route be:
/api/products
and the backend do the filter
or
/api/userid/products
and restrict access to others users products
You'll have the user_id from the JWT so for us we just use /api/products in our software. We avoid having multiple sources of data as that can create confusion and anything in the JWT is authenticated so we can trust that.
If I wanted to allow a user to see products from other users, I'd use /api/products/search?user_id=XXX for a flexible search system that could be expanded to search products by other criteria.
I find it better to make the user id part of the uri. Ultimately this is subjective, but it has a few advantages:
It makes really clear that the list of product in this case is a subordinate of users. There's no doubt it's the 'entire list' if it looks like a sub-resource.
It keeps the door open in the future to allow users to access product lists of other users.
I like the idea of giving every entity in my API a completely unique URI. To me, every user in your example has a personal product list and each product list is a unique resource.
As far as REST APIs go, which is the better structure to follow in general?
Assume GET/PUT/POST/DELETE for all resources.
1) Use currently signed in user for /users/**/* routes.
/users
/users/password
/users/email
/users/preferences
/users/documents
/documents/:id
2) Having absolute paths with IDs and using /users/:id for currently signed in user?
/users
/users/:id/password
/users/:id/email
/users/:id/preferences
/preferences/:id
Does it matter?
If the resource you are referring can be multiple, you should go with
/resource/resource_id
In the above case, user can only be the current person, so using a pattern like /users/user_id, sounds odd. Because, you will have to handle different cases like What if a logged in USER A triggers an api call with a different user-id USER B??
You can have a namespace like /profile for managing the email, name, image etc. You don't have to make it as /users/profile as it is implicit that the data is going to be manipulated / accessed for the current logged in user.
Both are fine. What's nice about creating unique endpoints for every user, is that one day you can allow user X to access information about user Y.
The pattern I followed in a recent API was to create a unique endpoint for every user, but 1 endpoint /current-user that redirects to the /user/:some-id.
A url might indicate an identity. It makes a lot of sense that other resources might refer to user as a 'creator' or 'modifier' of sommething, and in those places you might want to use a url (and not just a userId).
We have simple e-commerce website where we have several products. Currently, each product has "Place order" button.
When user clicks on this button, we show user a form to fill Name, Mobile number and address. We don't support any monetary transaction. Once user fills this form, the order is saved to database. The order table has OrderId, ProductId, UserName, UserMobile.
We are designing API to save the user order. Should we have association b/w product and order while designing this?
For example URI to save the user order should be like:
POST /api/products/1/lead/ - The request body has user information i.e. name,mobile,address. OR
POST /api/lead/ - The request body has "PRODUCT ID" and user information i.e. name,mobile,address.
I am confused whether productId should be in request URI or in the request body? How do we make such decision?
Given that
you're first navigating to a product, before actually placing the order
the product id has nothing in common with the UserInformation model that you're posting
I'd go with the first option: POST /api/products/1/lead/
I would always go with a more shallow route for representing resources, just for the sake of simplicity. No, a nested route isn't complicated or anything, but I've seen nesting go really far. So I would keep it as shallow as possible unless...
1) You plan on having more than one thing that can have a lead. For example, you can have a lead on a product:
api/products/1/lead
or a lead on a managed service that you all provide or something (I'm reaching right now):
api/managed_services/2/lead
You could pass that info in the body always, but I imagine it would become a little cumbersome to base what resource to create based on what properties were defined in the json.
2) You plan on breaking out that route and having it go to a different service eventually. Maybe this app will have to scale substantially and a ton of users will be hitting this route moreso than any other endpoint in the system. It's a lot easier to redirect all requests to a different microservice based on the url starting with api/products than it would be redirect based on the request body.
But honestly, I don't think it matters too much. As long as it's easy for your clients to consume.
I'm new to IdentityServer3 and have multiple MVC clients where users have claims which potentially could conflict and give undesired authorization.
Here is an example with two clients able to send emails and notifications to the users. A user might have access to both applications but should only be able to recieve notifications in Application A. How can we prevent the user from recieving notifications in Application B?
Application A
Claim Type: ApplicationFunctionality Claim Value:
RecieveNotifications
Claim Type: ApplicationFunctionality Claim Value: RecieveEmails
Application B
Claim Type: ApplicationFunctionality Claim Value: RecieveEmails
Would a resonable solution be to implement somekind of logic in a class using the IUserService interface?
Would it be correct to use claims in the way I described above across multiple clients, where we sometimes reuse claims for cross-client functionality. I guess it would require me to namespace the claims (perhaps using the requested scope name the client sends to IdentityServer) in order to distinguish claims for different clients and prevent unauthorized access between the clients.
Here is an example of user claim types/claim values:
Name: John Doe
Email: john.doe#acme.com
PreferedLanguages: English,Swedish,Spanish
ApplicationFunctionality: ClientA.RecieveEmails
ApplicationFunctionality: ClientB.RecieveEmails
ApplicationFunctionality: ClientA.RecieveNotifications
ApplicationFunctionality: ClientB.RecieveNotifications
ApplicationFunctionality: ClientA.ViewBackorders
ApplicationFunctionality: ClientA.DeleteBackorder
ApplicationFunctionality: ClientB.SearchProductInformation
CompanyID: 1145
CompanyID: 6785
CompanyName: Acme Inc
ApplicationLicense: ClientA.PayingNormalUser
ApplicationLicense: ClientB.FreeUser
A user from Company Acme Inc have several CompanyID's which is used when filtering data we request from webservices\databases in the data layer. The user have access to multiple applications where he/she could have different levels of functionality depending on what license they have bought in the applications. Some functionality exist in multiple clients but that doesn't mean the user are authorized to same functionality in all clients he/she have access to.
I would appreciate some guidelines about claims, or perhaps point me to some good resources about the subject. I have read that claims primary is used for identity related information (email, name, department, favorite color, shoe size etc), but if no role\permissions style claims should be used with claims then how should information about what a user is authorized to do in the clients be persisted and how should data be filtered in webservices/databases (resource providers) so the user only sees data he/she are authorized to see?
My first thoughts where that the id_token and access token would be convenient to use since they are issued by the STS (IdentityServer) and then later persisted in cookies. The STS would first be required to perform lookup of user account in Active Directory which contains user identity related information together with an lookup (using the username of the Active Directory user account) in custom database containing information about role\permissions and claims of the user.
How should I persist the roles/permissions and claims of a user if not using cookie persisted tokens provided by IdentityServer?
Clients (applications) and users can have their own set of claims. It looks like you want to have applications which have access to different resources. This is where you should use scopes. Basically define two scopes which define action on that resource (this is one common way of doing it) ie one for 'reading' emails and one for 'writing' emails (eg emails.read and emails.write) subsequently the other scopes can be backorders.read and backorders.delete. It's just good practice to have consistent naming here.
Ok now since weve defined those two scopes, you can now define two clients, one which has only the emails.read scope and the other one has both the read and write scopes. All this means is that one client has access to more resources than the other.
All the user identity claims should stick to the user itself. Application/client specific should never be associated with the user. Name,Email,ApplicationLicense, and PreferredLanguages are all valid claims for the user because they describe the user itself and what can be asserted about them.
For "complex" authorization you may want to look at this sample for some ideas on how to set up compound security policies or authorization.
It sounds like you want to expose a different value to different clients for the same claim. This seems like a logical thing to want to do, especially if you are integrating with clients that are not within your control and thus cannot dictate to them what to expect in each claim, or what scopes to request. A simple example might be the "roles" claim - you may wish to send different values depending on the app that's making the request. If you're joining someone else's enterprise, perhaps with multiple OpenID Connect Providers, you don't always have a choice over the scopes or claim names.
I feel that Nat Sakimura eludes to this in the OpenID Connect FAQ video, https://www.youtube.com/watch?v=Kb56GzQ2pSk (1 minute 40s), the idea that an entity might want to expose a different identity to different clients.
In terms of implementation, we've added a table with [identityId, clientId, attributeName, attributeValue] to allow us to store the same identity attribute for different clients. In our case, these identity attributes become claims in the outgoing JWT. As most attributes about a user are global (i.e. not client specific), we treat the data in this table as overrides to a base set, which saves duplicating the same data for each client unnecessarily. The iUserService.GetProfileDataAsync() method has access to the Client, so can tailor its response depending on the consumer of the data.