Java REST API Security – Effective Authorization Strategies

backendjavarestSecurity

There are a lot of questions on here that deal with the mechanics of authentication and authorization of RESTful APIs but none of them appear to go in to details of how to implement secure services at the application level.

For example let's say that my webapp (I've got Java in mind but this applies to any backend really) has a secure authentication system that allows users of the API to login with a username and password. When the user makes a request, at any point during the request processing pipeline I can call a getAuthenticatedUser() method which will return either the null user if the user isn't logged in, or a User domain object that represents the logged in user.

The API allows authenticated users to access their data e.g. a GET to /api/orders/ will return that user's list of orders. Similarly, a GET to /api/tasks/{task_id} will return data relating to that specific task.

Let's assume that there are a number of different domain objects that can be associated with a user's account (orders and tasks are two examples, we could also have customers, invoices etc.). We only want authenticated users to be able to access data about their own objects so when a user makes a call to /api/invoices/{invoice_id} we need to check that the user is authorized to access that resource before we serve it up.

My question is then, do patterns or strategies exist to deal with this authorization problem? One option I'm considering is creating a helper interface (i.e. SecurityUtils.isUserAuthorized(user, object)), which can be called during request processing to ensure that the user is authorized to fetch the object. This isn't ideal as it pollutes the application endpoint code with lots of these calls e.g.

Object someEndpoint(int objectId) {
    if (!SecurityUtils.isUserAuthorized(loggedInUser, objectDAO.get(objectId)) {
        throw new UnauthorizedException();
    }
    ...
}

…and then there's the question of implementing this method for every domain type which could be a bit of a pain. This might be the only option but I'd be interested to hear your suggestions!

Best Answer

Please for the love of God do not create a SecurityUtils class!

Your class will become 10k lines of spaghetti code in a matter of months! You would need to have an Action type (create, read, update, destroy, list, etc.) passed into your isUserAuthorized() method, which would quickly become a thousand-line-long switch statement with increasingly complex logic that would be difficult to unit test. Don't do it.


Generally what I do, at least in Ruby on Rails, is have each domain object be responsible for its own access privileges by having a policy class for each model. Then, the controller asks the policy class if the current user for the request has access to the resource or not. Here's an example in Ruby, since I've never implemented anything like it in Java, but the idea should come across cleanly:

class OrderPolicy

    class Scope < Struct.new(:user, :scope)

        def resolve

            # A user must be logged in to interact with this resource at all
            raise NotAuthorizedException unless user

            # Admin/moderator can see all orders
            if (user.admin? || user.moderator?)
                scope.all
            else
                # Only allow the user to see their own orders
                scope.where(orderer_id: user.id)
            end
        end
    end

    # Constructor, if you don't know Ruby
    def initialize(user, order)
        raise NotAuthorizedException unless user
        @user = user
        @order= order
    end

    # Whitelist what data can be manipulated by each type of user
    def valid_attributes
        if @user.admin?
            [:probably, :want, :to, :let, :admin, :update, :everything]
        elsif @user.moderator?
            [:fewer, :attributes, :but, :still, :most]
        else
            [:regualar, :user, :attributes]
        end
    end

    # Maybe restrict updatable attributes further
    def valid_update_attributes
    end

    # Who can create new orders
    def create?
        true # anyone, and they would have been authenticated already by #initialize
    end

    # Read operation
    def show?
        @user.admin? || @user.moderator? || owns_order
    end

    # Only superusers can update resources
    def update?
        @user.admin? || @user.moderator?
    end

    # Only admins can delete, because it's extremely destructive or whatever
    def destroy?
        @user.admin?
    end

    private

    # A user 'owns' an order if they were the person who submitted the order
    # E.g. superusers can access the order, but they didn't create it
    def owns_order
        @order.orderer_id == @user.id
    end
end

Even if you have complex nested resources, some resource must 'own' the nested resources, so the top-level logic inherently bubbles down. However, those nested resources need their own policy classes in the event that they can be updated independently of the 'parent' resource.

In my application, which is for my university's department, everything revolves around the Course object. It's not a User-centric application. However, Users are enrolled in Course, so I can simply ensure that:

@course.users.include? current_user && (whatever_other_logic_I_need)

for any resource that a particular User needs to modify, since almost all resources are tied to a Course. This is done in the policy class in the owns_whatever method.

I haven't done much Java architecture, but it seems that you could make a Policy interface, where the different resources that need to be authenticated must implement the interface. Then, you have all the necessary methods which can become as complex as you need them to be per domain object. The important thing is to tie the logic to the model itself, but at the same time keep it in a separate class (single responsibility principle (SRP)).

Your controller actions could look something like:

public List<Order> index(OrderQuery query) {

    authorize(Order.class)
    // you should be auto-rescuing the NotAuthorizedException thrown by
    //the policy class at the controller level (or application level)

    // if the authorization didn't fail/rescue from exception, just render the resource
    List<Order> orders = db.search(query);
    return renderJSON(orders);
}

public Order show(int orderId) {

    authorize(Order.class)
    Order order = db.find(orderId);
    return renderJSON(order);
}