6

I have implemented a ContainerRequestFilter that performs JWT-based authentication:

@Provider
@Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter {

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        AuthenticationResult authResult = ...
        if (authResult.isSuccessful()) {
            // Client successfully authenticated.
            // Now update the security context to be the augmented security context that contains information read from the JWT.
            requestContext.setSecurityContext(new JwtSecurityContect(...));
        } else {
            // Client provided no or an invalid authentication token.
            // Deny request by sending a 401 response.
            requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).build());
        }
    }
}

As you can see, I update the SecurityContext of the request, setting it to be an instance of my own custom implementation (JwtSecurityContext) if authentication succeeds. This implementation adds extra authentication and authorization data, which I would like to later access in my subsequent filter(s) and my resource methods.

I have also implemented an AuthorizationFilter that is invoked immediately after the AuthenticationFilter. Here, I can access the updated JwtSecurityContext just fine.

However, I am having problems when I try to inject the JwtSecurityContext into a resource (method).

I am currently using Jersey, and I've read the following in its documentation:

The SecurityContext can be directly retrieved from ContainerRequestContext via getSecurityContext() method. You can also replace the default SecurityContext in a request context with a custom one using the setSecurityContext(SecurityContext) method. If you set a custom SecurityContext instance in your ContainerRequestFilter, this security context instance will be used for injection into JAX-RS resource class fields. This way you can implement a custom authentication filter that may setup your own SecurityContext to be used. To ensure the early execution of your custom authentication request filter, set the filter priority to AUTHENTICATION using constants from Priorities. An early execution of you authentication filter will ensure that all other filters, resources, resource methods and sub-resource locators will execute with your custom SecurityContext instance.

I try to inject the JwtSecurityContext into a resource method like so:

@Path("/somepath")
public class SomeResource {
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public List<SomeItem> getItems(@Context SecurityContext securityContext) {
        // securityContext is of type 'SecurityContextInjectee'
    }
}

As the comment indicates, the runtime type of the securityContext variable becomes SecurityContextInjectee. From debugging, I've observed that this wraps a ContainerRequest which in turn wraps my JwtSecurityContext. However, there are no getters, and I do not want to use reflection to drill down this object hierarchy, so I don't know how to get a hold on my JwtSecurityContext.

I have tried changing @Context SecurityContext securityContext to @Context JwtSecurityContext jwtSecurityContext, but if I do this, the variable becomes null. I have also tried field injection, but this behaves the same way.

Am I heading down a wrong path? Should I not be accessing my custom SecurityContext in my resource method? One alternative could be to wrap all my data in the Principal implementation I return from getUserPrincipal in my JwtSecurityContext. I suppose the proxy (SecurityContextInjectee) would forward the call to its underlying JwtSecurityContext and hence return my Principal, but I am not sure, and ultimately I would prefer to use my JwtSecurityContext instead of wrapping these values in a Principal implementation.

Janus Varmarken
  • 2,306
  • 3
  • 20
  • 42
  • @peeskillet Interesting, I didn't know I could inject the ContainerRequestContext. I suppose I will be able to call getSecurityContext on that and have my custom security context returned. – Janus Varmarken Sep 14 '16 at 14:11
  • @peeskillet Yes, injecting ContainerRequestContext and calling getSecurityContext on that instance does indeed return the JwtSecurityContext. Thanks! If you like, you can post it as an answer and I'll accept it. – Janus Varmarken Sep 14 '16 at 14:18

1 Answers1

10

You can inject the ContainerRequestContext (as mentioned in this post) and just get the SecurityContext from there.

public List<SomeItem> getItems(@Context ContainerRequestContext context) {
    JwtSecurityContext sec = (JwtSecurityContext)context.getSecurityContext();
}
Community
  • 1
  • 1
Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
  • Thanks. Oh by the way, do you happen to know a thing or two about JPA? I am still looking for an answer for this question: http://stackoverflow.com/questions/38664913/jpa-locking-when-performing-an-indirect-insertion – Janus Varmarken Sep 14 '16 at 15:09
  • I know some JPA, but its not something I use everyday. I think that question is a little advanced for my knowledge :-( – Paul Samsotha Sep 14 '16 at 15:11
  • Okay, just thought it'd give it a shot :). Thanks again! – Janus Varmarken Sep 14 '16 at 15:12
  • For future reference: Unfortunately this strategy does not seem to work in sub-resource locators -- the type of the security context is WebComponentX on my Jersey+Glassfish setup. I would like to prevent access to the entire sub resource based on checking a condition in the my custom security context, but I guess I will just have to perform the check in all methods of the sub resource instead. – Janus Varmarken Oct 06 '16 at 10:55
  • @peeskillet, Any idea why there are two `SecurityContext`? I am mainly asking because I had to implement my own `SecurityContext` and set it during a `ContainerRequestFilter`, and I wanted to access it inside a `Singletong` bean later to find from another [answer](https://stackoverflow.com/questions/34747574/jersey-2-context-injection-based-upon-httprequest-without-singleton) for you that `SecurityContext` is `proxiable`, while `ContainerRequestContext` is not? – fujy May 15 '17 at 08:43
  • @fujy What about `ContainerRequest`? It the concrete implementation of `ContainerRequestContext`. If it doesn't work, try to inject it with `Provider` as mentioned [here](http://stackoverflow.com/a/37745362/2587435) – Paul Samsotha May 15 '17 at 09:23
  • @peeskillet Thanks for the fast reply, `ContainerRequest` didn't work, it stayed as the same object for all requests, so it had the same `SecurityContext ` object with the first entered authentication credentials. Actually `ContainerRequest` and `ContainerRequestContext` had exactly the same object instance, since as you said that it just a concrete implementation of the [later](https://github.com/jersey/jersey/blob/master/core-server/src/main/java/org/glassfish/jersey/server/ContainerRequest.java) – fujy May 15 '17 at 10:14
  • @peeskillet I tried the provider solution, but it didn't work, or maybe I didn't implement it the correct way, I tried again with `rs.core.SecurityContext`, I was able to see in debug that it is an instance of my own implementation `MySecurityContext` if I try to call any method inside it, but still I was not able to get a reference of it, so I tried casting, to find that Java doesn't allow casting of proxy classes, Last, I had to change `MySecurityContext.getUserPrincipal()` to return an object of a subclass of `java.security.Principal` that holds my customized user information, and it works. – fujy May 15 '17 at 16:55