Access control in microservices architecture

Hi all!

I’m thinking about how to model entity level access control. Because of performance and flexibility issues I think the best approach is to let each microservice manage access control for their own entities.

For code-reuse and easier maintenance i want to create a lagom project called “access-control” which provides support utilities for the projects with standard requirements.

This involves:

  • Apache Shiro dependency
  • Cassandra dependency
  • Utility methods to store rules
  • Utility methods to make access checks easy
    e.g. access.isPermitted(user, "organization:edit:1234).thenApply(…)

If i implement those helpers in a way that they are optimized for a read-side, i have the problem that a read side in lagom lags about 2 seconds due to the cassandra polling mechanism and if a client requests its own resource after creation, it may not have access to it yet.

Another possibility would be to design the library to be used in a ServiceCall method - then i could use it directly in the Request calls and delay the response to the user until the access - control update is completed.

An advantage of the read-side implementation would be some sort of transaction…

My questions are:

  • How do you think about this topic and the proposed solution?
  • Are there any known existing libraries?
  • Any other ideas or suggestions for this topic?

greetings,
Michael

1 Like

Very interested in hearing how others are approaching this subject. The way that we do things is we have an implementation modeled after Apache Shiro and we implement service-call level RBAC, so for instance:

def someCall(someId: IdType): ServiceCall[In, Out] = withAuthorization(forAction(someId, "read")) { 
implicit context => 
}

withAuthorization handles 401 or 403 responses for unauthenticated and unauthorized requests, respectively. Still, writing the permission logic is pretty tedious and it’s annoying to express things like “user-7” owns post-6 so user-7 automatically has posts:post-6:read in a low-boilerplate way.

Hi Andy

thanks for your response. I like your approch with the service composition call style aka authenticated(…). I think i will adopt this.

Could you please also drop a not on how you implemented the update of the model?

  • Meaning: Where and when do you update the roles/permissions of a user?
  • Do you keep the permissions in the affected service or do you use a centralized service which keeps all permissions?
  • Which database do you use? Would you mind sharing your db scheme of the permission storage table as an example.

Thanks a lot!

greetings,
Michael

Hello Michael,

I had the similar question posted while ago:

For my project, I decided to have one central microservice that is responsible for authorization, that act as a external api service.

Yes, of course I understand that it means I am introducing a single point of failure. But I think it is a fair trade-off because what I need right now is the faster time to develop the solution, and adding that authrozation to all the services increases the test complexity.

So in my K8s, only external api service is exposed to external world as a service. And it makes a synchronous invocation to other services on behalf of the caller, given that the user has access.

One added benefit with this approach is that because the UserAccessEntity will be used to maintain the session and will hold the encrypted password in it, I don’t have to worry about 2 seconds latency coming from the CQRS. And also because all the API calls come to this external api service, it is much easier to gather the analytics and try to get some insights on user behaviours.

I know this solution won’t scale for too large application, but for our purpose, this is pretty okay trade-off to live with.

Thanks

Joo

Hello, I think in this case(microservice architecture) the jwt token is the solution.

Hi all,

@guillaumebadin
Yes - i´m already using JWT. My question is more related to the architectural design of the authorization system itself. Querying as well as how and where to model updates to roles/permissions. As i want to be able to manage entity level permissions, i cannot put all of them in the JWT because the JWT would get too bit. therefore i´m looking for another solution.

@lejoow Ok yes i see. You use the user-entity (which holds the roles) as a cache. In my project the request-frequency will also be low enough that i could implement it that way. Thanks for your response!

Thanks,
Michael

We do per-service auth to avoid SPOF. It’s future based so if a service needs to ask another service for some information it is possible, though we try to avoid this.

Our roles/permissions are simple enough to store in a config file that is managed with the rest of our application configuration. If we needed to do it dynamically then I would likely change our scheme and would consider a gatekeeper service.