Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jax-rs rest webservice authentication and authorization

I have a web application that needs to allow users using different webclients (browser, native mobile app, etc) to register. After signing in they can access restricted content or their own content (like entries they create, etc).

What I did so far: I created a jax-rs rest webservice (I'm hosting my application on glassfish) that exposes the following methods:

  • register - user POST's his desired username/password/email/etc; if username/email is unique, an entry for this user is created in the database (I'm using Hibernate for persistence)
  • login - user POST's username and password. If they are ok a UUID is created and returned to the user (this will be used as a token for future requests). I have a table called logedusers, with userID, token, validSince as columns.

Here is where it gets confusing for me.

Let's say that I have another method, getUserEntries, that should return all the entries made by the user. To make this clearer, there will be a Entry table with the following fields: entryId, userId, text.

What is the best approach here?

What i do now, is I make a get request and pass in the token like this:

localhost:8080/myApp/getUserEntries?token=erf34c34

Afterwards, if the token is valid, I get the userID from the logedusers table and based on that userId, get all the entries and return them as json.

Something like this:

@GET
@Path("getUserEntries")
@Produces(MediaType.APPLICATION_JSON)
public Response getUserEntries(@QueryParam("token") String token) {      
    String userId=getUserIdFromToken(token);
    if (userId == null){
        return Response.status(Response.Status.UNAUTHORIZED).build();
    } else {
        //get some data associated with that userId, put it in the response object and send it back
        return Response.ok().entity(response).build();
    }
}

However, what happens if I have more methods that provide data if they are called by a valid user?

I'd have to do this check at the beginning of every method.

I want to make this authorization process transparent

So, two major questions here:

  1. Is this design ok? The whole authenticate with user/pass, server creates and stores and sends token to the user, user sends token on future requests.
  2. What do I do if i have many endpoints that need to determine the identity of the calling user? Can I mark them with some annotations, use some sort of security provider / authenticator (where I can add my own logic for validating - eg check to see if the token isn't older than 5 days, etc).

Thanks

like image 342
Timo89 Avatar asked Jan 15 '12 13:01

Timo89


People also ask

How do I authenticate a user in REST Web services?

Use of basic authentication is specified as follows: The string "Basic " is added to the Authorization header of the request. The username and password are combined into a string with the format "username:password", which is then base64 encoded and added to the Authorization header of the request.


1 Answers

Is this design ok? The whole authenticate with user/pass, server creates and stores and sends token to the user, user sends token on future requests.

It's somewhat OK. The conceptual level isn't too bad (provided you're OK with self-registration at all) but the interface needs a lot of tweaking. While yes, POST to register and login is correct, for the rest of your webapp you should be pulling the identity information out of the context if you need it, and using role-based access control at the method level where you can.

Note that your container has a whole set of authentication and authorization-support mechanisms built in. Use them.

What do I do if i have many endpoints that need to determine the identity of the calling user? Can I mark them with some annotations, use some sort of security provider / authenticator (where I can add my own logic for validating - eg check to see if the token isn't older than 5 days, etc).

Do they need the identity? Or do they just need to know that the user is allowed to access them? If the latter, the easiest method is to put a suitable @RolesAllowed annotation on the method, at which point (with suitable configuration; see the JEE5 security docs). If the former, you need to get the HttpServletRequest object for the current action and call its getUserPrincipal() method to get the user's identity (or null if they've not logged in yet). This SO question describes how to go about getting the request object; there are a few possible ways to do it but I recommend injection via a @Resource annotation.

What I wouldn't do is allow users to normally provide their own identity via a @QueryParam; that's just wildly open to abuse. You can allow them to ask about other users that way, but then you need to decide whether you are going to tell them anything or not based on whether the current user is permitted to know anything about the other user. That's the sort of complex security problem that comes up in a real app, and is a good point for needing the current verified user identity.

like image 95
Donal Fellows Avatar answered Oct 20 '22 04:10

Donal Fellows