This example demonstrates how to use the go-jwt-middleware with gRPC services.
- JWT authentication for gRPC unary methods
- Excluding specific methods from authentication (health checks)
- Retrieving validated claims in gRPC handlers
- Client-side token attachment using metadata
proto/- Protocol Buffer definitionsserver/- gRPC server with JWT middlewareclient/- Test client that makes authenticated requests
# Install dependencies
cd examples/grpc-example
go mod downloadIn one terminal:
go run server/main.goThe server will start on port 50051 with:
/greeter.Greeter/SayHello- Requires JWT authentication/greeter.Greeter/HealthCheck- Public endpoint (no auth)
In another terminal:
go run client/main.goThe client will:
- Call HealthCheck (no authentication)
- Try to call SayHello without a token (fails)
- Call SayHello with a valid JWT token (succeeds)
- Make multiple authenticated requests
gRPC server listening on :50051
- /greeter.Greeter/SayHello requires JWT authentication
- /greeter.Greeter/HealthCheck is public
=== Test 1: Health Check (no authentication) ===
Health check status: healthy
=== Test 2: SayHello without authentication (should fail) ===
Expected error: rpc error: code = Unauthenticated desc = missing credentials
=== Test 3: SayHello with valid JWT ===
Response: Hello World!
Authenticated as: user123
=== Test 4: Multiple authenticated requests ===
Hello Alice! (authenticated as: user123)
Hello Bob! (authenticated as: user123)
Hello Charlie! (authenticated as: user123)
=== All tests completed ===
// Create validator
jwtValidator, _ := validator.New(
validator.WithKeyFunc(keyFunc),
validator.WithAlgorithm(validator.HS256),
validator.WithIssuer(issuer),
validator.WithAudiences(audience),
)
// Create interceptor with excluded methods
jwtInterceptor, _ := jwtgrpc.New(
jwtgrpc.WithValidator(jwtValidator),
jwtgrpc.WithExcludedMethods("/greeter.Greeter/HealthCheck"),
)
// Add to gRPC server
grpcServer := grpc.NewServer(
grpc.UnaryInterceptor(jwtInterceptor.UnaryServerInterceptor()),
grpc.StreamInterceptor(jwtInterceptor.StreamServerInterceptor()),
)func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
// Get validated claims
claims, err := jwtgrpc.GetClaims[*validator.ValidatedClaims](ctx)
if err != nil {
return nil, status.Error(codes.Internal, "failed to get claims")
}
// Use claims data
subject := claims.RegisteredClaims.Subject
// ...
}// Add JWT to metadata
md := metadata.New(map[string]string{
"authorization": "Bearer " + token,
})
ctx := metadata.NewOutgoingContext(context.Background(), md)
// Make authenticated call
resp, err := client.SayHello(ctx, &pb.HelloRequest{Name: "World"})The interceptor supports several configuration options:
WithValidator()- Set the JWT validator (required)WithCredentialsOptional()- Allow requests without tokensWithExcludedMethods()- Skip auth for specific methods (static list)WithExclusionHandler()- Skip auth with custom logic (prefix matching, regex, etc.)WithErrorHandler()- Custom error handlingWithLogger()- Add logging
In production, combine with other interceptors using grpc.ChainUnaryInterceptor:
grpcServer := grpc.NewServer(
grpc.ChainUnaryInterceptor(
recoveryInterceptor,
loggingInterceptor,
jwtInterceptor.UnaryServerInterceptor(),
),
grpc.ChainStreamInterceptor(
recoveryInterceptor,
loggingInterceptor,
jwtInterceptor.StreamServerInterceptor(),
),
)This integration targets the standard google.golang.org/grpc server. If you are using
ConnectRPC, use the HTTP middleware instead since Connect
services are served over standard net/http handlers.
See the gRPC integration documentation for more details.