grpc-ecosystem / grpc-spring

Spring Boot starter module for gRPC framework.
https://grpc-ecosystem.github.io/grpc-spring/
Apache License 2.0
3.49k stars 821 forks source link

Config for OAuth2 JWT Security with Shared Keys #244

Open pluttrell opened 5 years ago

pluttrell commented 5 years ago

How would you configure the starter to work with OAuth2 JWTs using shared signing keys in a reactive gRPC service?

We see this example, but it's non-reactive, uses OpenId and uses a Keycloak server to validate the JWTs.

Here's more detail on our use case, which we think is pretty standard:

  1. We obtain JWTs from a Spring OAuth2 server.
  2. We supply the JWT as a bearer token in the Authorization header to each gRPC request.
  3. We need the server to the following on each call: a. Retrieve the JWT from the header. b. Validate the signature using a shared symmetric key that we'll provide to the service. c. Parse the JWT, extracting the authorities claim array. d. Set the Spring Security principal to include all authorities in the JWT.
  4. Then we'll be able to annotate our service methods with @PreAuthorize("hasAuthority('some-right')").

Also we're using reactive gRPC, so need to leverage Reactive Spring Security.

Thanks.

ST-DDT commented 5 years ago

Your setup/workflow looks good to me. Which part of it causes issues for you?

Also we're using reactive gRPC, so need to leverage Reactive Spring Security.

I'm not familiar with reactive spring security. How does it differ from the non-reactive one?

pluttrell commented 5 years ago

@ST-DDT Thanks for responding.

We need to setup/wire/configure the server to do step #3, so we're looking for info/docs/examples on how to configure the service with what the starter provides. The example provided implements a slightly different approach.

Regarding reactive spring security: Our gRPC services also leverage reactive-grpc and thus we can't trust ThreadLocal storage for security principals. Spring has means to address this, but they appear to be servlet filter based, which we don't have with gRPC, so I was hoping to find instructions for the above that also supported reactive streams.

ST-DDT commented 5 years ago

Regarding reactive spring security: Our gRPC services also leverage reactive-grpc and thus we can't trust ThreadLocal storage for security principals.

Even with non-reactive gRPC, you will still get yourself into deep trouble if you use thread locals for security.

If reactive gRPC still uses the same interceptor structure as non-reactive gRPC then will still work. For a start have a look at GrpcServerSecurityAutoConfiguration.

This class defines three important beans

  1. The exception handling
  2. The authentication handling
  3. The (custom) authorization handling

You don't have to do anything for 1..

For 2. authentication handling this is somewhat tricky. Lets explain how it works first. You have to define one (possibly nested) AuthenticationReader that will extract the credentials/the OAuth-Token from the request. You might be able to use the AuthenticationReader from the example. You just have to check that the used "token-container" is compatible with your AuthenticationManager/ AuthenticationProvider. (See also BearerAuthenticationReader)

If you also have a web-endpoint then you probably already have an AuthenticationManager that can be used validate/authenticate the Authentication (request). And here comes the tricky part. Some of the oAuth libraries hide that entire decoding and validation logic in the above-mentioned filters thus you cannot reuse it. If you give me a list of/pom with your security libraries I might be able to help you some more.

Finally 3. if use Spring's annotations then you don't have to do anything at all related to authorization checks (well except for spring's enable annotation security with proxy = true). However, it can be used if you encode the security policies in the proto files.

This library takes care of all the thread/context management for the security parts. Thus you can safely use SpringSecurityContextHolder or (grpc) Context.current().get(AuthenticatingServerInterceptor.AUTHENTICATION_CONTEXT_KEY) to get access to the authentication. Spring uses the former to check whether the current user has the permission to start the grpc call on that method (based on the annotation). However, it does not handle session-timeouts and logouts in the middle of streaming calls (spring-web doesn't do that either). But that isn't very complicated to implement at all, just add an if clause to the onNext() calls. It does not matter where you place the security annotations as long as spring gets its fingers in there (method invocations on spring managed bean references).

pluttrell commented 5 years ago

@ST-DDT Thanks for getting back to us.

The AuthenticatingServerInterceptor uses the SecurityContextHolder.getContext().setAuthentication(authentication) to set the Authentication in Spring Security. For Reactive Spring Security, we believe that ReactiveSecurityContextHolder should be used instead as shown in this example. As such, I think we can conclude that the starter doesn't support Reactive Streams out of the box. I'll add a separate ticket to request that support.

mattdkerr commented 4 years ago

@pluttrell that may be the same blocker we had for interceptors for tracing, there's a thread change as the boundary between application and hosted Orbit actor is hopped.

suveng commented 4 years ago

Thanks! That's what i need! When will it be released?

ST-DDT commented 4 years ago

Thanks! That's what i need! When will it be released?

Are you referring to reactive security support in this library? I can add it to the next release if you wish. Contributors are always welcome. 😉