Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add JWT support for gRPC server #75

Open
wants to merge 9 commits into
base: main
Choose a base branch
from

Conversation

Jotschi
Copy link

@Jotschi Jotschi commented Oct 30, 2023

Motivation:

This PR adds JWT support to the GrpcServer.

TODO:

  • Add usage doc

@vietj vietj added this to the 5.0.0 milestone Oct 30, 2023
@vietj
Copy link
Member

vietj commented Nov 1, 2023

Shouldn't we set an auth handler when we do register a handler instead of being global to the server ?

@Jotschi
Copy link
Author

Jotschi commented Nov 2, 2023

Shouldn't we set an auth handler when we do register a handler instead of being global to the server ?

Right. That would be better. I changed the API.

@Jotschi
Copy link
Author

Jotschi commented Nov 2, 2023

@vietj
I have squashed the commits of this PR and cherry-picked it into 4.x / 4.4 because I need this feature for my Vert.x 4.4.x project. This is just a headsup to let you know that this PR would also work for 4.x

@vietj
Copy link
Member

vietj commented Nov 2, 2023

can we also expect Oauth2 support and client support ?


User user();

GrpcServerRequest<Req, Resp> setUser(User user);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we don't need this I think

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the setter from the interface but the getter is required. Otherwise a user of the API is unable to access the authenticated user. At this point there is no "Context" object for gRPC so I put the user in the request.

@Jotschi
Copy link
Author

Jotschi commented Nov 4, 2023

can we also expect Oauth2 support and client support ?

I added token credential support to the client. As for Oauth2 support I don't feel comfortable to add it at this stage.

@vietj
Copy link
Member

vietj commented Nov 17, 2023

can you describe the issue for oauth2 ? it seems pretty ok to me, but pehraps I'm missing something

@Jotschi
Copy link
Author

Jotschi commented Nov 20, 2023

@vietj I took a look at the existing webclient implementation https://github.com/vert-x3/vertx-web/blob/master/vertx-web-client/src/main/java/io/vertx/ext/web/client/impl/OAuth2AwareInterceptor.java
I assume it would need to work in a similar way and basically intercept the calls and check whether the token needs to be refreshed. For this PR I would like to focus on JWT. Adding OAuth2 would mean creating an additional maven module. (eg. oauth2-grpc-client). I'm however getting a bit time constrained at the moment and can't add it at this point of time.

Copy link
Member

@vietj vietj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the contribution and sorry for the late review.

* @return a reference to this, so the API can be used fluently
*/
@GenIgnore(GenIgnore.PERMITTED_TYPE)
GrpcClient withCredentials(Credentials credentials);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be named credentials, we prefer to use withXXX in builders

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

@@ -59,6 +65,27 @@ public void handle(HttpServerRequest httpRequest) {
private <Req, Resp> void handle(MethodCallHandler<Req, Resp> method, HttpServerRequest httpRequest, GrpcMethodCall methodCall) {
GrpcServerRequestImpl<Req, Resp> grpcRequest = new GrpcServerRequestImpl<>(httpRequest, method.messageDecoder, method.messageEncoder, methodCall);
grpcRequest.init();
GrpcAuthenticationHandler authHandler = method.authHandler;
if (authHandler != null) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here we need to pause the grpc request and resume it after to avoid loosing messages

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this required because in ìnit additional handlers are being added?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in general any server request we receive should be paused then resumed if an asynchronous operation is achieved between its reception and usage

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can write a test for this, by sending messages in the client and have an authentication handler that delays the actual auth handler by a delay and check we got all messages on the server

}
}

private Future<String> parseAuthorization(HttpServerRequest req,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should use a result here and avoid relying on exception to communicate an authentication failure (and thus avoid GrpcException), the result should clearly be an enum like result and the gRPC code calling for authentication should set the corresponding status on the HTTP/2 response

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. Failures should only be used for unexpected states. I started rewriting this to use an enum as suggested:

     return Future.succeededFuture(GrpcAuthenticationResult.UNAUTHENTICATED);

I however don't see how I can return the token string in the same method via:

    return Future.succeededFuture(authorization.substring(idx + 1));

Do have an example or a pointer for me on how to solve this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will need to have a look deeper to make an elaborated suggestion here

GrpcClientRequestImpl<Req, Resp> call = new GrpcClientRequestImpl<>(request, messageEncoder, messageDecoder);
call.fullMethodName(service.getFullMethodName());
return call;
});
}

@Override
public GrpcClient credentials(Credentials credentials) {
if (credentials == null) {
Copy link
Member

@vietj vietj Dec 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think null could be accepted to null out credentials

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants