So I am trying to get my head around how to design my REST resources and how security fits into all of this. I am new to REST and user authentication in general so please bear with me - I am sure this is a really stupid question.
Now I know that you can define security roles for users, and restrict access to resources depending on whether they are an admin or not.
Where I am getting confused is say I have a situation where a certain user, 1234 say, has a list of subscriptions. Now only user 1234 should be able to access his subscriptions.
GET /user_id/1234/subscriptions
In this scenario, using roles doesn't make any sense, as you would have to define roles for each user. Do we have to control this access by doing some kind of check in the code to make sure this user has access? For example:
#Path("/user_id/{user_id}/subscriptions")
#GET
public getSubscriptions(#PathParam("user_id" int user_id))
{
if(user_id == "some code here that checks what the user_id of the current user is")
{
return Response.ok(getUserSubscriptionsFromDB(user_id));
}
else
{
return Response.status(Status.UNAUTHORIZED).build();
}
}
Is this how it is supposed to be done, or have i got it all wrong? If that is how you would go about it, what would the actual code in the "" look like? What object would I be interrogating to get my hands on the user_id? [The plan is in the future to use OAUTH2 social login for google, facebook etc.... but I might just use basic authentication too]
You basically got it right. The terminology for this type of authorization is Permission/Activity Based Authorization and is widely used for CRUD operations on recources like RESTful services.
A pseudo code would more look like this:
#Path("/users/{userId}/subscriptions")
#GET
public getSubscriptions(#PathParam("userId" int userId))
{
if(getSubject().isPermitted("subscriptions:read:"+userId)
{
return Response.ok(getUserSubscriptionsFromDB(userId));
}
else
{
return Response.status(Status.UNAUTHORIZED).build();
}
}
You might want to have a look on Apache Shiro which has pretty decent permission based authorization support.
You additionally might want to have a look on best practices for naming your REST resources.
Related
I have a Spring Boot REST API that I'm building. Im slightly stuck on the correct way to design my API in a way that protects each individual users' data. For example, consider the following database relations:
User -> (Has Many) Projects -> (Has Many) Tasks. (A User has-many Projects, and a Project has-many tasks).
For example, if I design my endpoints in the following way:
GET /api/v1/projects/{projectId}
POST /api/v1/projects/{projectId}/tasks
Just as a simple example for the above, how can I make sure, when creating new tasks for a certain project, that the project belongs to the logged in user?
Currently, I am using JWT tokens via Spring Security as my authentication strategy, and included in the payload of the token I have my Users' id. So with every request I can retrieve the user, but surely that's incredibly inefficient to be making so many requests to the database and check if the user actually has a given project.
Some solution I was thinking about is to simply have endpoints designed like this:
/api/v1/users/{userId}/projects/{projectId}/tasks
And then I can use the user id in the JWT payload and compare it to the user id in the request parameter. But then that would mean with every new relation in my database, the length of the url is going to be massive :) Also I guess it would mean all the business logic would be inside the User service for the whole application, right? Which seems a little odd to me... but maybe I'm wrong.
Im not sure if thats an issue or not, but just trying to design the API to be as elegant as possible.
Thanks again!
Checking if the user has permissions to a project on every request is the correct solution. Consider cases when many other applications / users are calling your API. You want to make sure that your API is as secure as possible and cannot be manipulated from the frontend.
To make it more efficient you should have a way/query to check associations in your database like a simple query that returns true/false which should be quicker than retrieving all the data and comparing in Java code.
And when possible combine multiple database queries into one, like for one of your examples:
GET /api/v1/projects/{projectId}
in this case, don't run a query to get a user's information and a query for the requested project. Instead you could do a single query with a join between the user's table and the project table which should only return a project if the user is associated with it. The best way really depends on how your database is structured.
Adding a user id into the API URL is just redundant information. Just because the user id in the token matches the user id in the URL doesn't mean the user has any kind of permissions to any project.
Another solution to be avoided is to include the user's project ids in the JWT token which you can then compare without making a database request. This is bad for several reasons:
The token should only have required information for the user to access the API, it shouldn't have business logic
Depending on how much business logic you store in the token the token can become large in size. See this post for a discussion on size limits: What is the maximum size of JWT token?
If there is a way for the someone other than the user (like admin) to add/remove a user's association to a project then that change will not be reflected in the token until the token's data is refreshed
EDIT:
On the spring side I have used the #PreAuthorize annotation before to handle these types of method checks. Below is pseudo code as an example.
#RestController
public class MyController {
#PostMapping
#PreAuthorize("#mySecurityService.isAllowed(principal, #in)")
public SomeResponseType api1(SomeRequestType requestData) {
/* this is not reached unless mySecurityService.isAllowed
returns true, instead a user gets a 401/403 HTTP response
code (i don't remember the exact one) */
}
}
#Service
public class MySecurityService {
/*
Object principal - this is spring's UserDetails object that is
returned from the AuthenticationProvider. So basically a Java
representation of the JWT token which should have the
user's username.
SomeRequestType requestData - this is the request data that was
sent to the API. I'm sure there is a way to get the project ID
from the URL here as well.
*/
public boolean isAllowed(Object principal, SomeRequestType requestData) {
/*
take the user's username from the principal, take the
project ID from the request data and query the database
to check authorization, return true if authorized
make this check efficient
*/
return false;
}
}
The annotation and the security service can then be applied to multiple methods. You can have many different security services depending on what your are checking.
There are other ways available too https://www.baeldung.com/spring-security-method-security and this has to be enabled in spring's configuration (also explained in the link).
Hi so if I understood it correctly you want to automatically assign the task that is going to be created with "POST /api/v1/projects/{projectId}/tasks" to the current logged in user.
You could try to add a Parameter 'Principal principal' to your rest controller. The Principal is the user that is sending the request.
After you have your Prinicipal, you could write a simple convert method(for example: convertPrincipalToUser(Principal principal) which returns you the user. Finally you can add your user to the corresponding task)
Here is some more information about it:
https://www.baeldung.com/get-user-in-spring-security
I've already read lots of topics about it, but still haven't found the better approach.
I have a User. One User may have many Posts. Users and Posts are different microservices. I'm using Spring Boot.
When the front-end call my Posts microservice sending a POST request to /posts/user/1, I need to check if the given userId (1) exists on my Users database. If no, throw an exception telling the front-end that the user doesn't exist. If yes, then insert the given request body as a Post.
The question is: how should I check this information at my backend? We don't want to let this responsibility with the front-end, since javascript is client-side and a malicious user could bypass this check.
Options:
REST communication between the microservices. (Posts microservice call Users microservice asking if the given id exists on his side)
Give Posts microservice access to Users microservice's database
I understand that communication between them will create coupling, but I'm not sure if giving Posts access to Users database is the best option.
Feel free to suggest any options.
You have an option to do interprocess communication between Post and User microservices through RESTful approach.
In case if you just want to check the existence of the resource and don't want any body in response then you should perfer using HEAD http method. Therefore your API endpoint hosted at User microservice will look like -
HEAD user/{userId}
Call this API from Post microservice.
Return 200 / OK if user exist
Return 404 / Not Found if user does not exist
Click here and here to get more details on HEAD method usage and use cases.
For this very particular use case, if you have a security layer, you can(should) make use of user access token, to ensure, that request is processed for the right user, which can be done by validating the token and relying on the fact that if user has token he exist. (As its just not about if user exist)
For any logic other than that, say you want to check if he is allowed to post or other such restrictions it is required to make a call to the user service.
Talking about giving access to the database, it will be against one basic guideline of microservices. Doing so will form a tight coupling between you and user. Its ok to call user service in this case which can decide how to serve this request.
User service on its part should provide ways to answer your queries within the SLA by caching or other mechanisms.
One more thing that you can explore is BFF (Backend for Frontend)
You rightly said you should not expose backend services to frontend or add any logic there, but often frontend pages may not be comfortable in accepting that content on same page is answered via n different back end services and there might be some logic to stitch such queries and thats where you can make use of BFF.
Backend server (in my case node) which take of things like these requiring frontend to make just one call(or less calls) for a given page and at the same time hiding your backend services within.
You're right, you must do a validation at the back end since, I suppose, it's a REST service and requests can be send not only from the UI.
Suppose you have a service implementation:
#Service
class UsersServiceImpl implements UsersService {
private final Users users;
public UsersServiceImpl(Users users) {
this.users = users;
}
#Override
public void addPost(long userId, Post post) {
User user = users.get(userId);
if (user == null) {
throw new UserNonExistent(userId);
}
user.addPost(post);
}
}
where Users is an interface representing a users database and UserNonExistent is a RuntimeException. Then in your controller you can do the following:
#RestController
class UsersController {
private final UsersService usersService;
public UsersController(UsersService usersService) {
this.usersService = usersService;
}
#PostMapping("/posts/user/{userId}")
public void addPostToUser(#PathVariable String userId, #RequestBody Post post) {
usersService.addPost(userId, post);
}
#ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = "User does not exist")
#ExceptionHandler({UsersService.UserNonExistent.class})
public void handleUserNonExistentException() {
// Nothing to do
}
}
If the supplied user ID is invalid handleUserNonExistentException() method will be invoked and it will return a BAD REQUEST HTTP status code.
I have a Spring boot API that I managed to secure with oAuth2 and Google.
I have something similar to this: Rest, Spring own OAuth2 server + OAuth2 providers like Facebook, Google, Yahoo
Everything works as expected.
My question is the following:
How can I know limit the access to certain users (not every google user) ?
This sounds like the authorization part to me. I'm not sure where to start as I'm a beginner in all this.
Thanks for any help or pointer.
To the best of my knowledge, if you want to limit access to an API based on a user's last name (going by your example) I will set up a sample scenario of something I might do.
Say for instance I have an API with an endpoint such as /api/family?lastname=doe and the purpose of this API is to return a list of people with the same last name as "doe" in this case. The API will only return a result if the last name provided in the parameter is equal to the current authorized user's last name.
So maybe I'd have an API set up as follows:
#RequestMapping(value="/api/family", method = RequestMethod.GET)
#ResponseBody
public List<Member> findFamilyMembersByLastName(#RequestParam(value="lastname", required=true) String lastName){
User authorizedUser = getCurrentUser(); // Set up a method that is able to get the current logged in user
if(!authorizedUser.getLastName().equals(lastName){
// Throw some kind of exception here
}
// otherwise perform a query to find a list of Members with last name and return them
}
I am very much new to web services. I have exposed some REST services using Jersey 2 in integration with Spring. Now I need to secure those rest services using authentication with username/password. I am told not to use Spring Security.
I have no idea of how to do this. I did search on the net but various links show various implementation and I am unable to decide how to proceed with it.
A common way for authenticating with username and password is to use Basic Authentication. Basically the client needs to send a request header Authorization, with the the header value as Basic Base64Encoded(username:password). So is my username is peeskillet and my password is pass, I, as a client, should set the header as
Authorization: Basic cGVlc2tpbGxldDpwYXNz
In a servlet environment, the container should have support for Basic authentication. You would configure this support on the web.xml. You can see an example in 48.2 Securing Web Applications of the Java EE tutorial. You will also notice in an example
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
That is for SSL support. This is recommended for Basic Authentication.
If you don't want to deal with the hassle of working with security domains and login modules, realm, and such, that would be required to customize the servlet support, or if you're just not in a servlet environment, implementing Basic Auth in a ContainerRequestFilter is really not too difficult.
You can see a complete example of how this could be done at jersey/examples/https-clientserver-grizzly. You should focus on the SecurityFilter
The basic flow in the filter goes something like this
Get the Authorization header. If it doesn't exist, throw an AuthenticationException. In which case the AuthenticationExceptionMapper will send out the header "WWW-Authenticate", "Basic realm=\"" + e.getRealm() + "\", which is part of the Basic Auth protocol
Once we have the header, we parse it just to get the Base64 encoded username:password. Then we decode it, then split it, then separate the user name and password. If any of this process fails, again throw the WebApplicationException that maps to a 400 Bad Request.
Check the username and password. The example source code just checks if the username is user and the password is password, but you will want to use some service in the filter to verify this information. If either of these fail, throw an AuthenticationException
If all goes well, a User is created from the authenticate method, and is injected into an Authorizer (which is a SecurityContext). In JAX-RS, the SecurityContext is normally used for authorization`.
For the authorization, if you want to secure certain areas for certain resources, you can use the #RolesAllowed annotation for your classes or methods. Jersey has support for this annotation, by registering the RolesAllowedDynamicFeature.
What happens under the hood is that the SecurityContext will be obtained from the request. With the example I linked to, you can see the Authorizer, it has an overridden method isUserInRole. This method will be called to check against the value(s) in #RolesAllowed({"ADMIN"}). So when you create the SecurityContext, you should make sure to include on the overridden method, the roles of the user.
For testing, you can simply use a browser. If everything is set up correctly, when you try and access the resource, you should see (in Firefox) a dialog as seen in this post. If you use cURL, you could do
C:/>curl -v -u username:password http://localhost:8080/blah/resource
This will send out a Basic Authenticated request. Because of the -v switch, you should see all the headers involved. If you just want to test with the client API, you can see here how to set it up. In any of the three cases mentioned, the Base64 encoding will be done for you, so you don't have to worry about it.
As for the SSL, you should look into the documentation of your container for information about how to set it up.
So this is really a matter what you would like to achieve. My case was to get this thing running with mobile and a One-Page-App JavaScript.
Basically all you need to do is generate some kind of header that value that will be needed in every consecutive request you client will make.
So you do a endpoint in which you wait for a post with user/password:
#Path("/login")
public class AuthenticationResource {
#POST
#Consumes("application/json")
public Response authenticate(Credentials credential) {
boolean canBeLoggedIn = (...check in your DB or anywher you need to)
if (canBeLoggedIn) {
UUID uuid = UUID.randomUUID();
Token token = new Token();
token.setToken(uuid.toString());
//save your token with associated with user
(...)
return Response.ok(token).type(MediaType.APPLICATION_JSON_TYPE).build();
} else {
return Response.status(Response.Status.UNAUTHORIZED).build();
}
}
}
Now you need to secure resource with need for that token:
#Path("/payment")
#AuthorizedWithToken
public class Payments {
#GET
#Produces("application/json")
public Response sync() {
(...)
}
}
Notice the #AuthorizedWithToken annotation. This annotaation you can create on your own using special meta annotation #NameBinding
#NameBinding
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface AuthorizedWithToken {}
And now for the filter that implements checking of the header:
#AuthorizedWithToken
#Provider
public class XAuthTokenFilter implements ContainerRequestFilter {
private static String X_Auth_Token = "X-Auth-Token";
#Override
public void filter(ContainerRequestContext crc) throws IOException {
String headerValue = crc.getHeaderString(X_Auth_Token);
if (headerValue == null) {
crc.abortWith(Response.status(Response.Status.FORBIDDEN).entity("Missing " + X_Auth_Token + " value").build());
return;
}
if(! TOKEN_FOUND_IN_DB) {
crc.abortWith(Response.status(Response.Status.UNAUTHORIZED).entity("Wrong " + X_Auth_Token + " value").build());
return;
}
}
}
You can create any number of your own annotations checking for various things in the http request and mix them. However you need to pay attention to Priorities but that actually easy thing to find. This method needs using https but that is obvious.
Security comes in two main flavours :
Container Based
application based
the standard way to secure spring applications is to use Spring Security (formerly Acegi).
It would be interesting to know why you're not being allowed to use that.
You could use container based security, but I'm guessing that your use of spring precludes that option too.
Since the choice of Spring is usually to obviate the need for the use of a full J2EE container (Edit : though as pointed out below by others, most ordinary servlet containers do allow you to implement various container based security methods)
This really only leaves you with one option which is to roll your own security.
Your use of Jersey suggests that this might be a REST application.
In which case you really ought to stick with standard HTTP Authentication methods that
comes in the following flavours in reverse order of strength :
BASIC
Digest
Form
Certificate
REST applications are usually supposed to be 'stateless', which essentially rules out form based authentication (because you'd require the use of Session)
leaving you with BASIC, Digest and Certificate.
Your next question is, who am I authenticating. If you can expect to know the username AND the password of the user based on what URL they requested (say if it's one set of credentials for all users) then Digest is the best bet since the password is never sent, only a hash.
If you cannot know the Password (because you ask a third party system to validate it etc.) then you are stuck with BASIC.
But you can enhance the security of BASIC by using SSL, or better yet, combining BASIC with client certificate authentication.
In fact BASIC authentication over HTTPS is the standard technique for securing most REST applications.
You can easily implement a Servlet Filter that looks for the Authentication Header and validates the credentials yourself.
There are many examples of such filters, it's a single self contained class file.
If no credentials are found the filter returns 401 passing a prompt for basic auth in the response headers.
If the credentials are invalid you return 403.
App security is almost an entire career in itself, but I hope this helps.
As the former posts say, you could go with different options, with a varying overhead for implementation. From a practical view, if you're going to start with this and are looking for a comfortable way for a simple implementation, I'd recommend container-based option using BASIC authentication.
If you use tomcat, you can setup a realm, which is relatively simple to implement. You could use JDBCRealm, which gets you a user and password from specified columns in your database, and configure it via server.xml and web.xml.
This will prompt you for credentials automatically, everytime you are trying to access your application. You don't have any application-side implementation to do for that.
What I can tell you now is that you already did most of the 'dirty' job integrating Jersey with Spring. I recommend to you to go an Application-based solution, is it does not tie you to a particular container. Spring Security can be intimidating at first, but then when you tame the beast, you see it was actually a friendly puppy.
The fact is that Spring Security is hugely customizable, just by implementing their interfaces. And there is a lot of documentation and support. Plus, you already have a Spring based application.
As all you seek is guidance, I can provide you with some tutorials. You can take advantage from this blog.
http://www.baeldung.com/rest-with-spring-series/
http://www.baeldung.com/2011/10/31/securing-a-restful-web-service-with-spring-security-3-1-part-3/
I working on Spring MVC app. The app funcionality is accessible through ReST API which jsp containing ajax logic consume. I am using spring security with defined roles (USER, COMPANY, ADMIN). Methods use requestMapping with responseBody such as:
www.app.com/auth/{userId}/request/{requestId}
It, of course, support GET for obtaining resource and POST for its creating or updating.
The problem is that after succesful login with, for example, userId = 1 I want GET request with requestId = 99. But when I run WebDev client for Chrome, I can also access another resource with easy request in format
www.app.com/auth/5/request/{requestId}
So basically, I can access resources, which I am not allowed to see. I hope you got the idea, where I am heading.
My question is - What is the best approach to secure this?
I was thinking about storing logged user Id (Integer) in session and comparing it everytime request for resource is made, but it seems to me that I am pulling the wrong end of rope :)
Thank you for any advice
You should have a look into the Expression-Based Access Control section of the spring security documentation.
Example copied from the documentation:
#PreAuthorize("#contact.name == authentication.name")
public void doSomething(Contact contact) {
..
}
This would check if name of the contact is equal to the name of the currently logged in user.
Using this this feature you can build much more sophisticated access rules than you could do with simple roles. However, this does not exclude using roles. You can still keep roles for basic security checks.