package middleware import ( "context" "strings" "github.com/golang-jwt/jwt/v5" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" ) type UserIDKey struct{} func AuthInterceptor(jwtSecret string) grpc.UnaryServerInterceptor { return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { md, ok := metadata.FromIncomingContext(ctx) if !ok { return nil, status.Errorf(codes.Unauthenticated, "metadata is not provided") } authHeader, ok := md["authorization"] if !ok || len(authHeader) == 0 { return nil, status.Errorf(codes.Unauthenticated, "authorization token is not provided") } tokenString := strings.TrimPrefix(authHeader[0], "Bearer ") token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { return nil, status.Errorf(codes.Unauthenticated, "unexpected signing method: %v", token.Header["alg"]) } return []byte(jwtSecret), nil }) if err != nil { return nil, status.Errorf(codes.Unauthenticated, "invalid token: %v", err) } if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid { userID, ok := claims["user_id"].(string) if !ok { return nil, status.Errorf(codes.Unauthenticated, "invalid token: user_id is not a string") } newCtx := context.WithValue(ctx, UserIDKey{}, userID) return handler(newCtx, req) } return nil, status.Errorf(codes.Unauthenticated, "invalid token") } }