GRPC Authentication and Authorization
In gRPC there are number of ways you can add authentication between client and server. It is handled via Credentials Objects.
There are two types of credential objects:
- Channel Credentials: These are handled on the channel level, i.e when the connection is established and a channel is created.
- Call Credentials: These are handled on per request level, i.e for every RPC call that is made.
These Credential objects can also be combined to create CompositeChannelCredentials with one Channel Credential and one Call Credential object.
Now let us see how we can use these credential objects.
Client Side TLS/SSL Authentication
gRPC provides a way to establish a connection without any secure connection i.e just like HTTP.
// client.go
conn, _ := grpc.Dial("localhost:5000", grpc.WithInsecure())
// server.go
lis, err := net.Listen("tcp", ":5000")
s := grpc.NewServer()
// ... register gRPC services ...
s.Serve(lis)
For secure communication we will create TransportCredentials which is a type of ChannelCredential object.
// client.go
creds, _ := credentials.NewClientTLSFromFile(certFile, "")
conn, _ := grpc.Dial("localhost:5000", grpc.WithTransportCredentials(creds))
// server.go
lis, _ := net.Listen("tcp", "localhost:50051")
creds, _ := credentials.NewServerTLSFromFile(certFile, keyFile)
s := grpc.NewServer(grpc.Creds(creds))
s.Serve(lis)
You can read more about generating own ssl certificates.
In case where you don't own the client, means you are creating a gRPC API for public use, you cannot give your certificate to every one using your client. In that case we rely on well known Certificate Authority like LetsEncrypt, Amazon, etc. to generate us a certificate. So let us change our client code a little.
// client.go
config := &tls.Config{
InsecureSkipVerify: false,
}
conn, err := grpc.Dial(address, grpc.WithTransportCredentials(credentials.NewTLS(config)))
// server code remains the same
In this case what happens is that grpc loads the certificates of well known Certificate Authorities from the OS and send it to server, hence no need to manually provide a certificate.
Token Based Authentication / OAuth2
Many a times we want to differentiate a client by issuing them different tokens. TLS Authentication is good way to secure your connection but it does not tell us from which client the request is coming from. We will send the token in request metadata just like HTTP Headers.
gRPC has "google.golang.org/grpc/metadata"
package to send and receive metadata with a RPC request.
// client.go
// add metadata to request context
md := metadata.Pairs("Authorization", "Bearer xxx-xxx-xxx")
ctx := metadata.NewContext(context.Background(), md)
// use this context to call rpc
_, err = client.CallRPC(ctx, requestObject)
We will use UnaryInterceptor on server which acts like a middleware and checks for the token for all the requests.
// create a middleware
func AuthInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
meta, ok := metadata.FromContext(ctx)
if !ok {
return nil, grpc.Errorf(codes.Unauthenticated, "missing context metadata")
}
// Take care: grpc internally reduce key values to lowercase
if len(meta["authorization"]) != 1 {
return nil, grpc.Errorf(codes.Unauthenticated, "invalid token")
}
if meta["authorization"][0] != "xxx-xxx-xxx" {
return nil, grpc.Errorf(codes.Unauthenticated, "invalid token")
}
return handler(ctx, req)
}
// pass this when creating server
server := grpc.NewServer(
grpc.UnaryInterceptor(AuthInterceptor),
)
But above code only works for non-streaming RPCs. For Streaming RPCs you can implement StreamInterceptor. Instead of implementing it again you can use go-grpc-middleware package.
Hope this article helps you with authentication in gRPC.
If you are interested in working with cutting-edge technologies, come work with Vernacular.ai. Apply on angel.co.