go 43 lines · 1 tab

gRPC unary interceptor for auth and timing logs

Leah Thompson Jan 2026
1 tab
package grpcmw

import (
  "context"
  "time"

  "go.uber.org/zap"
  "google.golang.org/grpc"
  "google.golang.org/grpc/codes"
  "google.golang.org/grpc/metadata"
  "google.golang.org/grpc/status"
)

type ctxKey string

const principalKey ctxKey = "principal"

func Principal(ctx context.Context) (string, bool) {
  v := ctx.Value(principalKey)
  s, ok := v.(string)
  return s, ok
}

func UnaryAuth(log *zap.Logger, validate func(token string) (string, error)) grpc.UnaryServerInterceptor {
  return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
    start := time.Now()
    md, _ := metadata.FromIncomingContext(ctx)
    token := ""
    if vals := md.Get("authorization"); len(vals) > 0 {
      token = vals[0]
    }

    principal, err := validate(token)
    if err != nil {
      return nil, status.Error(codes.Unauthenticated, "unauthenticated")
    }
    ctx = context.WithValue(ctx, principalKey, principal)

    resp, hErr := handler(ctx, req)
    log.Info("grpc.unary", zap.String("method", info.FullMethod), zap.Duration("duration", time.Since(start)), zap.Error(hErr))
    return resp, hErr
  }
}
1 file · go Explain with highlit

Interceptors are the cleanest way to standardize cross-cutting behavior in gRPC. I use a unary interceptor to extract authorization from metadata, validate it, attach the principal to context.Context, and log the method name and duration. This keeps service methods focused: they read principal from context and do work. The subtle benefit is that all endpoints share the same auth policy and logging format, so your logs are consistent and greppable. I also keep the error surface tight: unauthenticated requests return codes.Unauthenticated and we don't leak whether the token was missing vs invalid. Combined with metrics and tracing interceptors, this becomes a strong baseline for production-grade gRPC services.


Related snips

Share this code

Here's the card — post it anywhere.

gRPC unary interceptor for auth and timing logs — share card
Link copied