Implementing Authentication Scheme in Python

393 views
Skip to first unread message

James Telzrow

unread,
Feb 28, 2021, 12:35:11 PM2/28/21
to grpc.io
Hello,

I am continuously getting the following error when making a gRPC call:

grpc._channel._InactiveRpcError: <_InactiveRpcError of RPC that terminated with:
status = StatusCode.UNIMPLEMENTED
details = "Method not found: GetVersion/GetVersion"
debug_error_string = "{"created":"@1614530914.104036000","description":"Error received from peer ipv4:192.168.0.69:9998","file":"src/core/lib/surface/call.cc","file_line":1068,"grpc_message":"Method not found: GetVersion/GetVersion","grpc_status":12}"

I am making the call using this bit of Python code:

def runFunc():
     channel = grpc.insecure_channel('192.168.0.69:9998')
     stub = grpcBisq_pb2_grpc.GetVersionStub(channel)
     response = stub.GetVersion(grpcBisq_pb2.GetVersionRequest())
     print(response.version)

However, I know that the server has a GetVersion gRPC service with a GetVersion endpoint that takes a GetVersionRequest, since both the client and server have the following in their .proto file:

service GetVersion {
    rpc GetVersion (GetVersionRequest) returns (GetVersionReply) {
}
}
message GetVersionRequest {
}
message GetVersionReply {
     string version = 1;
}

The server is written in Java (and wasn't written by me) and I wrote my client in Python. However, the server has an example client (also written in Java) that uses some sort of authentication scheme, and as of now my Python client isn't using any type of authentication. I'm guessing this is what is causing the server to return the above error. In that Java client, the stub is created as follows:

public final class GrpcStubs {
     public final GetVersionGrpc.GetVersionBlockingStub versionService;
     public GrpcStubs(String apiHost, int apiPort, String apiPassword) {
          CallCredentials credentials = new PasswordCallCredentials(apiPassword);
          var channel = ManagedChannelBuilder.forAddress(apiHost, apiPort).usePlaintext().build();
          Runtime.getRuntime().addShutdownHook(new Thread(() -> {
               try {
                    channel.shutdown().awaitTermination(1, SECONDS);
               } catch (InterruptedException ex) {
                    throw new IllegalStateException(ex);
               }
          }));
          this.versionService = GetVersionGrpc.newBlockingStub(channel).withCallCredentials(credentials);
     }
}

PasswordCallCredentials is defined in PasswordCallCredentials.java as follows:

/**
* Sets the {@value PASSWORD_KEY} rpc call header to a given value.
*/
class PasswordCallCredentials extends CallCredentials {
     public static final String PASSWORD_KEY = "password";
     private final String passwordValue;
     public PasswordCallCredentials(String passwordValue) {
          if (passwordValue == null)
               throw new IllegalArgumentException(format("'%s' value must not be null", PASSWORD_KEY));
          this.passwordValue = passwordValue;
     }
     @Override
     public void applyRequestMetadata(RequestInfo requestInfo, Executor appExecutor, MetadataApplier metadataApplier) {
          appExecutor.execute(() -> {
               try {
                    var headers = new Metadata();
                    var passwordKey = Key.of(PASSWORD_KEY, ASCII_STRING_MARSHALLER);
                    headers.put(passwordKey, passwordValue);
                    metadataApplier.apply(headers);
               } catch (Throwable ex) {
                    metadataApplier.fail(UNAUTHENTICATED.withCause(ex));
               }
           });
     }
     @Override
     public void thisUsesUnstableApi() {
     }
}

If I understand correctly, PasswordCallCredentials is subclassing CallCredentials, and when called, it causes the key-value pair "password: {apiPasswordHere}" to be placed in the header of every gRPC request. If my understanding is wrong, please correct me.

I have tested the Java client, and it works perfectly.

Unfortunately, even after thoroughly searching through the documentation, I do not understand how to implement this in Python. I know I need to create a grpc.secure_channel and pass a ChannelCredentials object, but I'm not sure how to proceed from there. Can someone help me?

Sanjay Pujare

unread,
Feb 28, 2021, 1:14:33 PM2/28/21
to James Telzrow, grpc.io
This one https://grpc.github.io/grpc/python/_modules/grpc.html#metadata_call_credentials talks about metadata call credentials which seems to be similar to what the Java client is doing. Examples of authMetadataPlugin are available at https://www.programcreek.com/python/example/113599/grpc.AuthMetadataPlugin

Although I doubt the error "Method not found: GetVersion/GetVersion" is because of missing CallCredentials.



--
You received this message because you are subscribed to the Google Groups "grpc.io" group.
To unsubscribe from this group and stop receiving emails from it, send an email to grpc-io+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/grpc-io/eec9603c-2dc5-45a4-bfd1-f0d5ef796059n%40googlegroups.com.

James Telzrow

unread,
Feb 28, 2021, 3:37:36 PM2/28/21
to grpc.io
I understand that metadata_call_credentials() takes a AuthMetadataPlugin object and returns a proper CallCredentials object. What I don't understand is how to create an AuthMetadataPlugin that performs the same action as PasswordCallCredentials's applyRequestMetadata() method in the Java implementation above: adding a key-value pair to the request header.

Furthermore, it appears that the Java client implementation above creates a plain text channel, and then passes in the PasswordCallCredentials object when creating the stub. However, I don't believe this is possible using the Python gRPC API. Instead, credentials must be passed upon channel creation, and the credentials must be ChannelCredentials, not CallCredentials or any other type. So even if I knew how to create a AuthMetadataPlugin that could put the proper values in the request header, I still couldn't use metadata_call_credentials() to do what I want, since that method returns a CallCredentials object, whereas I need a ChannelCredentials object.

Am I missing something?

James Telzrow

unread,
Feb 28, 2021, 3:55:21 PM2/28/21
to grpc.io
If this is the case and that functionality (and/or the functionality to specially authenticate requests as in the Java code above) simply isn't available in the gRPC Python API, might I have to create a sort of "intermediary" in Java, that is a client to my final destination and a server to my Python script, that listens for requests from my Python script, sends the equivalent but properly authenticated request to the server, and then returns back the results?

And if you doubt the "Method not found" error is because of missing CallCredentials, what else do you think it may be?

On Sunday, February 28, 2021 at 1:14:33 PM UTC-5 sanjay...@google.com wrote:

James Telzrow

unread,
Feb 28, 2021, 5:39:58 PM2/28/21
to grpc.io
I refactored my .proto file to resolve the "Method not found" error. Now, this code:

def runFunc():
     channel = grpc.insecure_channel('192.168.0.69:9998')
     stub = grpc_pb2_grpc.GetVersionStub(channel)
     response = stub.GetVersion(grpc_pb2.GetVersionRequest())
     print(response.version)

gives me the following error:

"grpc_message":"missing 'password' rpc header value","grpc_status":16

I know exactly what the problem is. As you can see in PasswordCallCredentials in the working example Java implementation above, "password" and passwordValue are being added to the header of each request. My code is not doing that, and I am completely at a loss as to how to do so.

Can someone help me?

Sanjay Pujare

unread,
Feb 28, 2021, 5:46:36 PM2/28/21
to James Telzrow, grpc.io
Okay so the "method not found" error is unrelated to missing call-credentials and you fixed it by fixing the proto file I assume.

I thought this doc might be very helpful to describe how to plumb the plugin. For how to add metadata (i.e. the headers in your gRPC call) this https://github.com/grpc/grpc/tree/master/examples/python/metadata  - specifically https://github.com/grpc/grpc/blob/master/examples/python/metadata/metadata_client.py might be helpful?

James Telzrow

unread,
Mar 2, 2021, 12:18:25 AM3/2/21
to grpc.io
Yes, the "method not found" issue was unrelated. That metadata_client.py example was very helpful and is exactly what I needed; my code now works perfectly. Thank you for your help!
Reply all
Reply to author
Forward
0 new messages