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

38 Authorization with gRPC Interceptors

IH
Ihsan Arif
Writer at Santekno · Backend Engineer

gRPC has become one of the backbones of modern communication between microservices. Its appeal? Speed, efficiency, and a mature ecosystem. However, once a service starts handling requests from a variety of clients with different access rights, one big issue arises: AUTHORIZATION.

If you hardcode your authorization rules directly into each service handler, problems quickly follow: duplication, code that is hard to maintain, and even hidden security bugs. This is exactly why gRPC Interceptors are such an elegant solution—they act as middleware, allowing you to embed an authorization layer consistently across every endpoint in a clean and testable way.

In this article, I will cover:

  • The basic concept of gRPC Interceptors
  • What is the difference between “Authorization” and “Authentication”?
  • A scheme for implementing authorization with Interceptors
  • A simple authorization code example in Go
  • Simulating authorization scenarios
  • A summary of best practices

Authentication vs. Authorization

Although they sound similar, the two are different. Let’s clarify.

AuthenticationAuthorization
Who are you?Is this user granted access?
Usually via JWT/TokenRole, permission, policy, etc.
Often responds 401/UnauthOften responds 403/Forbidden

In most modern applications, authentication happens first. Once the authorization header passes, authorization is then performed to verify whether the user is allowed to access a particular method/endpoint.

In gRPC, you can embed these two steps in separate interceptors, or combine them into a single sequential interceptor.


Getting to Know gRPC Interceptors

Put simply, a gRPC Interceptor is a middleware function that runs before the main method is invoked. It can be installed for:

  • Unary Interceptor: For request-response endpoints
  • Stream Interceptor: For streaming endpoints (client/server/bidirectional)

For authorization, the Unary Interceptor is the one most commonly used.

A simple flow diagram of authorization within an interceptor can be illustrated as follows:

MERMAID
flowchart TD
    req[Permintaan masuk ke gRPC Service]
    auth[Autentikasi Token/Identitas]
    authorize[Otorisasi: Pengecekan role/permission]
    handler[Eksekusi Handler utamanya]
    reject[Return Error Forbidden]
    
    req --> auth
    auth -- valid --> authorize
    auth -- invalid --> reject
    authorize -- allowed --> handler
    authorize -- denied --> reject

Case Study: A gRPC Service with Authorization

Suppose we have two methods on a Book service:

  • GetBook : Anyone who is logged in may access it
  • DeleteBook : Only users with the admin role may access it

Protobuf Service

proto
1service BookService {
2    rpc GetBook(GetBookRequest) returns (BookResponse) {}
3    rpc DeleteBook(DeleteBookRequest) returns (DeleteBookResponse) {}
4}

Implementing an Authorization Interceptor in Golang

Many languages support gRPC, but in the cloud (and startup) ecosystem, Go is a popular choice. Below is an example of a Unary Interceptor for simple authorization using JWT.

1. Decode the Token & Extract the Role

go
 1// A helper function to decode the JWT (without an external library)
 2func extractRoleFromToken(authHeader string) (string, error) {
 3    // Assume the format "Bearer eyJhbGciOiJIUzI1NiIsInR5cC..."
 4    split := strings.Split(authHeader, " ")
 5    if len(split) != 2 || split[0] != "Bearer" {
 6        return "", errors.New("invalid authorization header")
 7    }
 8    // Simulate: the role is encoded in the payload section after "."
 9    parts := strings.Split(split[1], ".")
10    if len(parts) != 3 {
11        return "", errors.New("malformed jwt")
12    }
13    // In a real use case: parse JSON, base64-decode, etc.
14    // Demo: role 'admin' if token == "admin.token"
15    if split[1] == "admin.token" {
16        return "admin", nil
17    }
18    return "user", nil
19}

2. Mapping Permissions to Methods

To keep things clean and flexible, use a map:

go
1var methodPermission = map[string]string{
2    "/BookService/DeleteBook": "admin",
3    // Other methods, if you want to add a specific role
4}

3. The Authorization Interceptor

go
 1import (
 2    "google.golang.org/grpc"
 3    "google.golang.org/grpc/metadata"
 4    "context"
 5)
 6
 7func AuthorizationInterceptor(
 8    ctx context.Context,
 9    req interface{},
10    info *grpc.UnaryServerInfo,
11    handler grpc.UnaryHandler,
12) (interface{}, error) {
13    // Get the metadata (HTTP header)
14    md, ok := metadata.FromIncomingContext(ctx)
15    if !ok {
16        return nil, status.Error(codes.Unauthenticated, "metadata missing")
17    }
18
19    // Get the Authorization header
20    authHeaders := md["authorization"]
21    if len(authHeaders) == 0 {
22        return nil, status.Error(codes.Unauthenticated, "authorization header missing")
23    }
24
25    userRole, err := extractRoleFromToken(authHeaders[0])
26    if err != nil {
27        return nil, status.Error(codes.Unauthenticated, "invalid token")
28    }
29
30    // Check whether the role is sufficient for this method
31    needRole, ok := methodPermission[info.FullMethod]
32    if ok && userRole != needRole {
33        return nil, status.Error(codes.PermissionDenied, "forbidden")
34    }
35
36    // If it passes, forward to the handler
37    return handler(ctx, req)
38}

4. Attach the Interceptor to the Server

go
1grpcServer := grpc.NewServer(
2    grpc.UnaryInterceptor(AuthorizationInterceptor),
3)
4// register server, etc...

Simulating Scenarios: Access & Denied

Scheme Table

MethodTokenInferred RoleOutcome
GetBookBearer xxxuserAllowed
DeleteBookBearer xxxuserDenied
DeleteBookBearer admin.tokenadminAllowed

Pseudo-Test

go
 1// Simulate a request to /BookService/DeleteBook
 2
 3ctx := metadata.NewIncomingContext(context.Background(), metadata.Pairs(
 4    "authorization", "Bearer admin.token",
 5))
 6_, err := AuthorizationInterceptor(ctx, &DeleteBookRequest{}, &grpc.UnaryServerInfo{
 7    FullMethod: "/BookService/DeleteBook",
 8}, HandlerStub)
 9fmt.Println(err) // <nil>, allowed
10
11ctx2 := metadata.NewIncomingContext(context.Background(), metadata.Pairs(
12    "authorization", "Bearer user.token",
13))
14_, err = AuthorizationInterceptor(ctx2, &DeleteBookRequest{}, &grpc.UnaryServerInfo{
15    FullMethod: "/BookService/DeleteBook",
16}, HandlerStub)
17fmt.Println(err) // PermissionDenied, forbidden

Full Flow Diagram

MERMAID
sequenceDiagram
    participant Client
    participant Interceptor
    participant Handler
    Client->>Interceptor: gRPC call + header authorization
    Interceptor->>Interceptor: Validasi token & mapping method to role
    alt Token invalid / tak cukup role
        Interceptor-->>Client: Error 401 / 403
    else Token valid & role sesuai
        Interceptor->>Handler: teruskan request
        Handler-->>Client: Return response
    end

Best Practices & Notes

  • Separate AuthN and AuthZ: Interceptors can be chained, so keep authentication and authorization separate to make maintenance easier.
  • Granular: The implementation can be built per-method, per-resource, or even based on dynamic logic.
  • Extensible: Integrate with an Identity Provider (OAuth, OIDC, etc.) when you are production-ready.
  • Audit & Logging: Add logging for every authorization failure.
  • Test Coverage: Create test cases for every combination of role and endpoint.

Conclusion

Authorization with gRPC Interceptors is an industry pattern that is scalable, testable, and elegant—especially in a microservices architecture. Layered security can be built without polluting your handler/core logic.
Start with a static mapping like the one above, then move on to dynamic rules as your organization’s needs grow.

What about you? Is your authorization already modular and maintainable enough across your production gRPC services? If not, perhaps now is the time to refactor—and the Interceptor is your best partner.


References:

Feel free to share your experiences or questions in the comments!

Related Articles

💬 Comments