fix(grpc): return 401 for unauthenticated v1 gateway errors (#11786)

Fixes #11730

The v1 gRPC middleware can return native gRPC status errors such as
`codes.Unauthenticated` when the auth header is missing.

Those errors were being passed through the generic gRPC error converter,
which only handled ZITADEL errors and downgraded raw gRPC statuses to
`codes.Unknown`.

Through grpc-gateway that caused `/auth/v1/users/me` to return HTTP 500
instead of HTTP 401.

So this change preserves native gRPC status errors in
`ZITADELToGRPCError`, keeps `ExtractZITADELError` aligned with those
transport codes for activity reporting, and adds regression coverage for
the converter, middleware, and gateway HTTP behavior.

---------

Co-authored-by: Gayathri Vijayan <[email protected]>
This commit is contained in:
Oluwatobi Mustapha
2026-04-10 12:22:40 +00:00
committed by GitHub
co-authored by Gayathri Vijayan
parent 035ac4b9a2
commit fbd43ced6e
4 changed files with 80 additions and 0 deletions
@@ -7,7 +7,9 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/protoadapt"
commandErrors "github.com/zitadel/zitadel/internal/command/errors"
@@ -49,6 +51,14 @@ func TestCaosToGRPCError(t *testing.T) {
}
}
func TestZITADELToGRPCError_PreservesGRPCStatus(t *testing.T) {
err := ZITADELToGRPCError(t.Context(), status.Error(codes.Unauthenticated, "auth header missing"))
grpcStatus, ok := status.FromError(err)
require.True(t, ok)
assert.Equal(t, codes.Unauthenticated, grpcStatus.Code())
assert.Equal(t, "auth header missing", grpcStatus.Message())
}
func Test_getErrorInfo(t *testing.T) {
tests := []struct {
name string
@@ -205,6 +215,14 @@ func Test_extractError(t *testing.T) {
"",
slog.LevelError,
},
{
"grpc unauthenticated",
args{status.Error(codes.Unauthenticated, "auth header missing")},
codes.Unauthenticated,
"auth header missing",
"",
slog.LevelWarn,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {