Quarkus and Keycloak: evaluate scope based permission - java

I have a REST API, that should use Keycloak for authorizing incoming requests. I have problems with configuring desired scopes in my API.
In Keycloak I defined a client for my API, a client for the calling service and two users. Both users have some realm roles. The client for my API defines a resource, some scopes (eg read, delete), policies and permissions.
One permissions allows an user with the admin role to perform scopes read and delete on the resource. The other allows an user with the monitoring role to read the resource.
I understand that the path of my endpoint will be checked against the path defined in the Keycloak client resource.
My concrete problem here is that I don't know how to tell my endpoint, which scopes he needs.
In the end I want to have multiple endpoints with the same path but different scopes (for each HTTP method).
Here is my example config:
quarkus.http.port=8080
quarkus.resteasy.path=/api
quarkus.oidc.auth-server-url=https://myUrl:8443/auth/realms/enaq
quarkus.oidc.client-id=rest-api
quarkus.oidc.credentials.secret=secret-string
quarkus.oidc.enabled=true
quarkus.application.name=keycloak-test-api
quarkus.application.version=0.0.1
quarkus.keycloak.policy-enforcer.enable=true
Here is an example implementation:
#Path("/measurements")
public class MeasurementResource {
#Autowired
MeasurementService delegate;
#GET
#Path("/{id}/{from}/{to}/{resolution}")
#Produces(MediaType.APPLICATION_JSON)
public MeasurementHistory getMeasurementHistory(#PathParam("id") String deviceId, #PathParam("from") Long from,
#PathParam("to") Long to, #PathParam("resolution") Integer resolution) {
return delegate.getMeasurementHistory(from, to, resolution, deviceId);
}
}
In my example my permissions will be evaluated, but only the admin gets access. The monitoring user will be denied, although he should be able to read.

In order to define the exact scopes that should be protected and map them to the appropriate HTTP verb, you should configure the policy enforcer as documented here.
There is no documentation on Quarkus side yet that shows how to map that to properties (application.properties) but it should be quite trivial to do so. You can grab an example from here.
Another option is to set the quarkus.keycloak.policy-enforcer.http-method-as-scope=true. By doing that, your resources would be associated with scopes such as GET, POST, DELETE, etc. And the policy enforcer will enforce access accordingly without forcing you to map paths in your configuration file.

Related

Why do we need exactly the same configuration in resource and Authorization server

I'm talking about the case when these two are separate apps. I'm not interested in merging them in one app.
So, in a authorization server we extend AuthorizationServerConfigurerAdapter class and in resource server ResourceServerConfigurerAdapter and in both we create exactly the same beans like JwtAccessTokenConverter, DefaultTokenServices etc. but mostly I don't get why do we need TokenStore in both.
Does this mean that we store for example in memory the same token in different applications?
What's the best approach to remove this code duplication? Create a library for common classes? Make request to auth server to validate the token? But how are we going to extract more info from JWT token if we don't have the decoding logic in resource server?
Does this mean that we store for example in memory the same token in different applications?
https://auth0.com/learn/json-web-tokens/
This is a stateless authentication mechanism as the user state is never saved in the server memory. The server’s protected routes will check for a valid JWT in the Authorization header, and if there is, the user will be allowed. As JWTs are self-contained, all the necessary information is there, reducing the need of going back and forward to the database.
What's the best approach to remove this code duplication? Create a library for common classes?
If you use a symmetric key:
#Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("123");
return converter;
}
JwtAccessTokenConverter, DefaultTokenServices etc will be identical beans in both resource server and authentication server, so you could have a common project for both with the declarations of these beans, and add them as a dependency in both projects.
But, if you use an asymmetric KeyPair, the beans declaration changes completely and they couldn't be the same.
You can see more information about this difference here:
https://www.baeldung.com/spring-security-oauth-jwt
Make request to auth server to validate the token?
JWT's main advantage is not having to do that.
But how are we going to extract more info from JWT token if we don't have the decoding logic in resource server?
If you use a symmetric key, you can decoding logic in resource server.
The best way to resolve this case in the microservice system - is to create some entities: API composer, authorization service and business services.
Base mechanism of this scheme is:
Firstly, you separate your requests with unauthorized and authorized with a token header. Usually, it's named something like "X-AUTHORIZATION-HEADER" or anything like this. In this header, you put your JWT-token and send it on the server's gateway, which role is performing 'API Composer' - It's some kind of router, which accept requests, and delivery them to the appropriate recipients.
In particular, API composer accepting a response, parsing headers, finding the appropriate header with a token, and sending it to Auth Service and receiving a response with user or error. And in this scheme, you need entities like JwtAccessTokenConverter and else only in Auth Service
Then, aggregated response payload will be complete, your API will send the response to the client.
I use this scheme when I developing my microservice systems, for me it's working fine.
Hope, I correctly understood your question and my answer is will help you) Best Regards.

Implementing Collaborative Authentication in Java Web Application

I've a requirement to build a Java based web application where a resource should be available only when all the authorized users of that resource are logged in. Also, if any authorized user logs out, the resource should no longer be available to any of them.
The resource could be of any type(html pages, pdf documents, spread sheets etc.,)
Is there any existing authentication standards/protocols that supports this type of requirement or I've to build this from scratch?
the resource should be available only when all the authorized users of that resource are logged in. Also, if any authorized user logs out, the resource should no longer be available to any of them.
Once you have given access to the resource to an user, this user will be able to download / take screenshots / save / record the resource, no matter if it's a PDF document, an image, an audio file. I don't know the context and the goal of what you're trying to build, but you should know that it will be insecure in any case.
Even putting this consideration aside, you'll need a real-time solution. Once the user has loaded the page containing the resource, you need to be able to hide or deny modification rights to him. This means you have to use something like WebSockets or Ajax Polling on the client side to have the frontend know when your server considers that not all the required users are online, and that the access to the resource should be "denied". But once more since this is client-side code it can easily be changed or altered, the requests it is sending can easily be blocked by the user, so it is once again inherently insecure.
I'd suggest giving a little bit of context here and describing what is the problem you're trying to solve, because most likely there's a more reasonable solution to solve it.
If what you need to do is to deny modification rights if not all the "resource owners" are online, it is more easily doable since the modifications will happen on the server side. In this case, a solution using WebSockets could quite easily be implemented but I don't know a library or framework that does such a thing. Most likely you will have to build it yourself.
If you're not constrained to use a specific web framework, feel free to try the following filter based implementation for jersey. Note that you still need to add a fair amount of custom code for handling the logic of "Collective authentication" as jersey only provides the basic tools required for this, and it doesn't explicitly implement the whole concept. Here's how you could do it, on a high level:
class AuthorizationProvider {
public void authenticate(ContainerRequestContext requestContext) {
// Here you would need to query your database to get the Collection of Users belonging
// to the "Collective" Role. You would then check if they are all logged in.
// A really abstract version would look like this, assuming you've already queried the DB
// and have a reference to the above mentioned Collection.
if (collectiveUsers.size == collectiveUsers.stream().filter(User::isLoggedIn).count()) {
return true;
}
return false;
}
}
class AuthorizationRequestFilter implements ContainerRequestFilter {
private final AuthorizationProvider authorizationProvider;
#Override
public void filter(ContainerRequestContext requestContext) {
if (authorizationProvider.authenticate(requestContext)) {
// serve whatever it is you want to serve if all required users are logged in
} else {
// otherwise reject the request
requestContext.abortWith(Response
.status(Response.Status.UNAUTHORIZED)
.entity("Resource available only after collective login")
.build());
}
}
}
#ApplicationPath("/")
class MyApplication extends ResourceConfig {
public MyApplication() {
// Register the filter
register(AuthorizationRequestFilter.class);
}
}
Apart from this, you would also need to handle the Login part.
You would assign these specific users the Collective role, and you would mark them as logged in, whenever they successfully pass through login authentication.
If all the above conditions are met, you should be able to successfully serve your "Collective only" page, only when all "Collective" users are logged in.
This also covers the part where if either one of these users logs out, you store the state in your database (mark the Collective user with isLoggedIn = false). So from this point on, whenever somebody requests the page, it will return Unauthorized.
Conversely, you can also attempt to implement SSE (Server sent events) to actively update the frontend part, if somebody logs out. With this, the page will actively be disabled even if somebody has already managed to get it previously.
Container request filter source and example, for reference, jersey docs

Custom PermissionEvaluator for authorizing resource access for each REST Resource

I have implemented a REST application with some complicated authorization requirements.
Here's a summary
My customers purchase a proxy device called Collector that enables their home automation control to be centralized. My customers also purchase multiple home automation devices (let's call them HADevices) that report their metrics through the collector to my REST application.
An admin(who is my customer service rep), with role ROLE_ADMIN, should be able to look at any data from any Collector or HADevice. A customer, with role ROLE_USER role, should only be able to view data about the Collector or an HADevice that s/he owns.
The Collector, with role ROLE_COLLECTOR is the only role authorized to insert data i.e. create or update a resource in my REST service. Let's call this url /deviceMetrics (POST). A Collector can insert metrics for any HADevice associated with the customer. HADevices have no role and do not interact with my REST application. A Collector can only insert records to HADevices that have the same customer as the Collector.
I am using spring security 4.0 for authentication and #Secured annotation for authorization. However, I find that my code is cluttered with repetitive permission validations which take up a majority of my logic. The basic insertions and retrievals are pretty straightforward.
I want to use a PermissionEvaluator to centralize Access Control. I have to secure the following urls
GET /collectors/{id}/deviceMetrics - I retrieve the user from the
Principal and verify that the Collector with id={id} in my spring
data repository belongs to the Principal and if not I send a 403
GET/hadevices/{id}/deviceMetrics - I retrieve the user from the
Principal and verify that the HADevice with id={id} in my spring data
repository belongs to the Principal and if not I send a 403.
POST /collectors/{id}/deviceMetrics - I retrieve the Collector uniqueId
from the Principal and make sure that the Collector's id matches the
{id} in the URL
POST /hadevice/{id}/deviceMetrics - I retrieve the
Collector uniqueId from the Principal and the associated Customer. I
also pull the Customer associated with HADevice with id={id} and
compare the two. If they are unequal, I send a 403.
My application is littered with such complex authorization requirements for each REST resource, and I want to use a custom org.springframework.security.access.PermissionEvaluator, specifically by implementing the following method.
boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission)
I'm planning to use a combination of targetType and request.getUrl() to get a specialized Evaluator for each url and resource.
Is there a better way to do this?
Your question is quite broad, but I think you can get away with quite simple logic for most cases.
GET /collectors/{id}/deviceMetrics
Given that you have a DeviceMetrics class with suitable properties, you can annotate your data repository with something like:
#PostAuthorize("hasRole('ROLE_ADMIN') or (hasRole('ROLE_USER') and returnObject.collector.owner = authentication.name)")
public DeviceMetrics getDeviceMetrics(long deviceId);
(This assumes that DeviceMetrics class has a property collector which has a property owner which is the username.)
That doesn't need a PermissionEvaluator at all. Maybe you need one for more complex cases:
POST /collectors/{id}/deviceMetrics
#PreAuthorize("hasRole('ROLE_COLLECTOR') and hasPermission(#collectorId, 'com.example.Collector', 'WRITE')")
public void saveDeviceMetrics(long collectorId, DeviceMetrics deviceMetrics);
You only need one PermissionEvaluator since you get all the information you need as method arguments.
For those who are wondering what my solution looks like, I borrowed from this example.
It's old and it's based on xml configuration which I am not particularly fond of. But the idea is to create a Map and initialize the custom PermissionValidator and to store the authorization logic in the Permission interface implementations.
The biggest pain point was injecting an autowired HashMap of tuples, but that's an implementation detail that reasonably experienced spring users can figure out.

JAX-RS and resource ownership restrictions

There are many good resources and literature on how to set up a a JAX-RS API. However, I have yet to find any resource that properly describes how to do security restrictions towards specific resources and methods. For example, given a resource PictureResource, only the uploader of the picture (and an admin) should be able to delete or change properties related to the picture, while anyone should be able to view the picture. The admin restriction is fine as it can be solved by roles, however the uploader would depend on the context of the call. A token identifying the user would then describe who is making the call. This can be solved through a ContainerRequestFilter.
#Path("pictures/{pictureId}")
public class PictureResource {
#GET
public Response getPicture(#PathParam("pictureId") final int pictureId) {
// Get the picture, available for all users.
}
#DELETE
public Response deletePicture(#PathParam("pictureId") final int pictureId) {
// Delete the picture, only available for the uploader of the picture and admins.
}
// ...
}
What would be the JAX-RS way of solving this issue? I'm assuming this can be solved by annotations, but is is rather vague to me how to do this. Would another approach be to dynamically assign the user a pictureOwnerRole depending on the context of the call?
The problem is discrete resource access control. You need a standard way to describe the resource being accessed in terms of ownership. Who owns the resource, and who has been granted scoped authority over it.
The problem is that this is very domain specific. Resource grouping and ownership requires the ability to lookup a resource instance or associated metadata and determine ownership/access requirements.
I don't know of any security frameworks that provide a standard framework or annotation for this.
You could place pictures into a directory structure and use directory access control to determine what users have what access to the resources.
Something like #Secured("ownerDecider=PictureInspector.class") would be how I would approach it. The AccessDecisionVoter or AfterInvocationProvider in spring security could then use the provided strategy for discerning ownership, and restrict access accordingly.

Authenticating with an API key in JAX-RS

We'd like to secure our rest api using an api key. Here are the requirements:
Public-facing services require an api key.
"Private" services can only accept a call from within the cluster,
not the outside world.
Each api identifies a user, and the User object must be available to
the rest service.
Is there some standard way to do this in a JAX-RS app? (We're using Resteasy.)
I've read all about filters, interceptors and basic auth, but it isn't clear to me what's the best approach.
In an earlier version of the app we had a roll-your-own solution in which public services ran on a public port and private ones on a private port. There was a custom api key lookup that set the User object as a variable into the rest service object.
I can't figure out how to do either of these things using standard JAX-RS.
Using a filter to intercept the request
This kind of authentication could be achieved with a ContainerRequestFilter, intercepting the requests to your resource methods.
The filter will be used to extract the API key from the request and validate it. If the API key is not valid, the request will be refused. Otherwise, the request will proceed to the resource methods.
Have a look at the following piece of code. The ContainerRequestContext API can be used to extract information from the HTTP request:
#Provider
#Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
// Extract and validate the API key from the request
String apiKey = requestContext.getHeaderString("API-Key");
...
}
}
Also have a look at this answer I wrote a while ago about authentication with tokens in JAX-RS. There you will find plenty of details that can be useful to address the situation you described in your question.
Identifying the user
During the authentication process, you must be able to identify the user who is performing the request. To propagate this information to your resource classes/methods you could:
Override the SecurityContext and inject it into your resource classes/methods.
Use a CDI Event and a producer method to create an object that contains the user identifier that can be injected in your resource classes/methods.
For more details on the these approaches, refer to the answer I mentioned above.
Binding the filter to some resource classes/methods
By default, the filters are global (it means they are executed for all the resource methods of your application). To bind the filter to a subset of resource methods or classes, you could use name binding annotations.
Not giving a detailed answer, but just a suggestion. Check for CustomInvokers and register the invoker for the services. Validate the api-key and throw an error if it's not valid. If there is an error then your client gets an error. The Service code won't be called.
For the actual security framework, please check netflix zuul.

Categories