Skip to content
Santekno.com | Tech Tutorials and Trends
EN
📖 0%
05 Aug 2025 · 6 min read ·Article 58 / 110
Go

58. gRPC with JWT for Authentication

IH
Ihsan Arif
Writer at Santekno · Backend Engineer

58. gRPC with JWT for Authentication

gRPC and JWT are two popular technologies for building modern microservices that are both secure and scalable. In this 58th article, I’ll share best practices for implementing gRPC with JWT for authentication, complete with code examples, diagrams, and a simple simulation.


Why gRPC & JWT?

When building a microservice architecture that demands high performance and cross-language interoperability, gRPC is often the go-to choice. gRPC uses Protocol Buffers for data serialization, making inter-service communication lightweight and fast. Even so, security remains a top concern, especially when it comes to authentication & authorization.

This is where JWT (JSON Web Token) comes in as a popular and effective authentication solution. JWT is easy to use, lightweight, and stateless, which makes it a perfect fit for distributed systems and microservices.


Authentication Flow Diagram for gRPC with JWT

Let’s start with a quick overview of the authentication process for a gRPC service using JWT:

MERMAID
sequenceDiagram
    participant Client
    participant AuthService
    participant GRPCServer

    Client->>AuthService: Request Token (user & password)
    AuthService-->>Client: Return JWT Token
    Client->>GRPCServer: gRPC Request (attach JWT)
    GRPCServer->>GRPCServer: Validate JWT Token
    GRPCServer-->>Client: Response (if valid)

JWT Flow in gRPC

  1. User Login
    The client sends a username and password to the AuthService.
  2. Token Generation
    The AuthService creates and returns a JWT if the login succeeds.
  3. Request to the gRPC Service
    The client sends a gRPC request to the service it wants to access, attaching the JWT as metadata/header.
  4. JWT Validation
    The gRPC service validates the JWT before executing the business logic.
  5. Authorized Response
    If the token is valid, the service processes the request and returns a response.

Code: Implementing JWT Auth in gRPC (Golang)

I’ll use Go here, since its ecosystem is mature for implementing gRPC. The principles are the same in other languages.

1. Building the JWT Authentication Service

Let’s start with a simple AuthService: it generates a JWT when the credentials are correct.

go
 1// auth_service.go
 2package main
 3
 4import (
 5    "net/http"
 6    "time"
 7
 8    "github.com/golang-jwt/jwt/v4"
 9    "github.com/gin-gonic/gin"
10)
11
12var jwtKey = []byte("secret_mykey")
13
14type Credentials struct {
15    Username string `json:"username"`
16    Password string `json:"password"`
17}
18
19func GenerateJWT(username string) (string, error) {
20    claims := &jwt.RegisteredClaims{
21        Subject:   username,
22        ExpiresAt: jwt.NewNumericDate(time.Now().Add(2 * time.Hour)),
23    }
24
25    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
26    return token.SignedString(jwtKey)
27}
28
29func Login(c *gin.Context) {
30    var cred Credentials
31    if err := c.BindJSON(&cred); err != nil {
32        c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid input"})
33        return
34    }
35    // Hardcoded user as an example; use a DB in production!
36    if cred.Username == "alice" && cred.Password == "password123" {
37        tokenString, _ := GenerateJWT(cred.Username)
38        c.JSON(http.StatusOK, gin.H{"token": tokenString})
39    } else {
40        c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
41    }
42}
43
44func main() {
45    r := gin.Default()
46    r.POST("/login", Login)
47    r.Run(":8080")
48}

2. Defining the gRPC Service (Proto)

protobuf
 1// service.proto
 2syntax = "proto3";
 3
 4service Greeter {
 5  rpc SayHello (HelloRequest) returns (HelloReply) {}
 6}
 7
 8message HelloRequest {
 9  string name = 1;
10}
11
12message HelloReply {
13  string message = 1;
14}

Generate the Go code with:

text
1protoc --go_out=. --go-grpc_out=. service.proto

3. Implementing the JWT Interceptor in gRPC

The interceptor validates the JWT in the metadata of every request.

go
 1// server.go
 2package main
 3
 4import (
 5    "context"
 6    "errors"
 7    "fmt"
 8    "log"
 9    "net"
10
11    "google.golang.org/grpc"
12    "google.golang.org/grpc/metadata"
13    "github.com/golang-jwt/jwt/v4"
14
15    pb "path/to/your/proto"
16)
17
18var jwtKey = []byte("secret_mykey")
19
20func validateJWT(tokenString string) (string, error) {
21    token, err := jwt.ParseWithClaims(tokenString, &jwt.RegisteredClaims{}, func(token *jwt.Token) (interface{}, error) {
22        return jwtKey, nil
23    })
24    if err != nil {
25        return "", err
26    }
27    if claims, ok := token.Claims.(*jwt.RegisteredClaims); ok && token.Valid {
28        return claims.Subject, nil
29    }
30    return "", errors.New("invalid token")
31}
32
33// Unary interceptor
34func authInterceptor(
35    ctx context.Context,
36    req interface{},
37    info *grpc.UnaryServerInfo,
38    handler grpc.UnaryHandler,
39) (interface{}, error) {
40    md, ok := metadata.FromIncomingContext(ctx)
41    if !ok {
42        return nil, fmt.Errorf("missing metadata")
43    }
44
45    tokens := md["authorization"]
46    if len(tokens) == 0 {
47        return nil, fmt.Errorf("authorization token is required")
48    }
49    jwtToken := tokens[0]
50    _, err := validateJWT(jwtToken)
51    if err != nil {
52        return nil, fmt.Errorf("invalid token: %v", err)
53    }
54
55    // Forward to handler
56    return handler(ctx, req)
57}
58
59// gRPC Service Implementation
60type server struct {
61    pb.UnimplementedGreeterServer
62}
63
64func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
65    return &pb.HelloReply{Message: "Hello " + in.Name}, nil
66}
67
68func main() {
69    lis, err := net.Listen("tcp", ":50051")
70    if err != nil {
71        log.Fatalf("failed to listen: %v", err)
72    }
73    s := grpc.NewServer(grpc.UnaryInterceptor(authInterceptor))
74    pb.RegisterGreeterServer(s, &server{})
75    log.Println("gRPC server started on :50051")
76    if err := s.Serve(lis); err != nil {
77        log.Fatalf("failed to serve: %v", err)
78    }
79}

4. Simulation: Calling the gRPC Service with a JWT

A. Obtaining a Token

bash
1curl -X POST http://localhost:8080/login \
2-H "Content-Type: application/json" \
3-d '{"username":"alice", "password":"password123"}'

Response:

json
1{
2  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6..."
3}

B. Calling gRPC with a JWT (Go client example)

go
 1import (
 2    "context"
 3    "log"
 4    "google.golang.org/grpc"
 5    "google.golang.org/grpc/metadata"
 6    pb "path/to/your/proto"
 7)
 8
 9func main() {
10    conn, _ := grpc.Dial("localhost:50051", grpc.WithInsecure())
11    defer conn.Close()
12    client := pb.NewGreeterClient(conn)
13
14    jwtToken := "<token-from-the-previous-step>"
15    md := metadata.New(map[string]string{"authorization": jwtToken})
16    ctx := metadata.NewOutgoingContext(context.Background(), md)
17
18    resp, err := client.SayHello(ctx, &pb.HelloRequest{Name: "Alice"})
19    if err != nil {
20        log.Fatal("Error: ", err)
21    }
22    log.Println(resp.Message)
23}

Table: Comparing gRPC Auth Options

Auth OptionStatelessEasy to IntegrateWeb Pages (Cookie)MicroserviceRecommended Use
gRPC + BasicNoEasyYesLess SecureInternal service testing
gRPC + API KeyYesEasyNoFairly SecurePublic APIs, low risk
gRPC + JWTYesVery EasyNot directlyVery StrongMicroservices in production
gRPC + OAuth2YesComplexYesVery StrongOpenAPI, 3rd party, SSO

Best Practice Tips

  • Use HTTPS: Protects against sniffing as the JWT travels between nodes.
  • Token Expiry: Always set an expiration time for your JWTs to avoid leaving tokens vulnerable.
  • Key Rotation: Rotate jwtKey regularly, and use asymmetric JWT signing at larger scale.
  • Secure Claims: Never put sensitive data inside a JWT!
  • Logging & Error Handling: Log every failed authentication request for auditing purposes.

Conclusion

Implementing gRPC with JWT for authentication is one of the foundations of secure, scalable, and stateless microservices. By leveraging an interceptor, we can keep every gRPC endpoint protected from unauthorized access. And remember: security isn’t just about technique, it’s also about discipline in maintaining your application’s ecosystem!

If you’d like the complete source code or have further questions, feel free to leave a comment! 👋


I hope this was helpful. Don’t forget to share it if you found it useful! 🚀

Related Articles

💬 Comments