Repository: open-telemetry/opentelemetry-collector (Go)

Issue:
`otlpreceiver` is not throwing a valid `gRPC` `Status` for successful scenarios or authentication-related errors
**Describe the bug**
OTLP receiver is not throwing a valid `gRPC` `Status` for successful scenarios or authentication-related errors.

**Steps to reproduce**
1. Enable the OTLP receiver configuration as below for the Collector 
(Please note you need this [extension](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/extension/oidcauthextension) to enable authentication with the receiver)
```
receivers:
  otlp:
    protocols:
      grpc:
        auth:
          authenticator: oidc
      http: 
        auth: 
          authenticator: oidc
```

2. Send sample traces to the collector endpoints in each port using both HTTP and gRPC SDK implementations with a sample JWT tokens.
OTLP/HTTP is 4318
OTLP/gRPC is 4317

**What did you expect to see?**
`gRPC Status:16` for unauthenticated requests and `gRPC Status:0` for successful authentication.

**What did you see instead?**
HTTP will throw proper status codes like `401(Unauthorized)` for errors and `200` for successful authentication.
But with gRPC, it will throw `gRPC Status:2` for errors and nothing for successful authentication.

**What version did you use?**
Version: v0.76.1

**What config did you use?**
Config: N/A

**Environment**
N/A

**Additional context**
[Temporary fix](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/ca15907071b8d24e03f1ae03dfb71abe4ae68440/extension/oidcauthextension/extension.go#L48) for error scenarios
```
errNoAudienceProvided = errors.New("no Audience provided for the OIDC configuration")
....
```
to 
```
import (
        ....
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

errNoAudienceProvided = status.Error(codes.Unauthenticated, "failed to verify the token")
....
```

Suggestion: I think it's better if we can fix [interceptors](https://github.com/open-telemetry/opentelemetry-collector/blob/a8a01efbb713d67ce58315aa1a3ec22d36adc7a0/config/configgrpc/configgrpc.go#L448) in `configgrpc` similar to `confighttp` [interceptor](https://github.com/open-telemetry/opentelemetry-collector/blob/a8a01efbb713d67ce58315aa1a3ec22d36adc7a0/config/confighttp/confighttp.go#L357)
 
I was not able to find a any solution for the successful scenario




Discussion:

Patch:
diff --git a/.chloggen/jpkroehling-grpc-statuscode.yaml b/.chloggen/jpkroehling-grpc-statuscode.yaml
new file mode 100644
index 00000000000..d78ef4c7a9a
--- /dev/null
+++ b/.chloggen/jpkroehling-grpc-statuscode.yaml
@@ -0,0 +1,12 @@
+# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
+change_type: 'enhancement'
+
+# The name of the component, or a single word describing the area of concern, (e.g. otlpreceiver)
+component: configgrpc
+
+# A brief description of the change.  Surround your text with quotes ("") if it needs to start with a backtick (`).
+note: gRPC auth errors now return gRPC status code UNAUTHENTICATED (16)
+
+# One or more tracking issues or pull requests related to the change
+issues: [7646]
+
diff --git a/config/configgrpc/configgrpc.go b/config/configgrpc/configgrpc.go
index 2bffbe4993f..5b85b4f5f2c 100644
--- a/config/configgrpc/configgrpc.go
+++ b/config/configgrpc/configgrpc.go
@@ -17,12 +17,14 @@ import (
 	"go.opentelemetry.io/otel"
 	"google.golang.org/grpc"
 	"google.golang.org/grpc/balancer"
+	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/credentials"
 	"google.golang.org/grpc/credentials/insecure"
 	"google.golang.org/grpc/encoding/gzip"
 	"google.golang.org/grpc/keepalive"
 	"google.golang.org/grpc/metadata"
 	"google.golang.org/grpc/peer"
+	"google.golang.org/grpc/status"
 
 	"go.opentelemetry.io/collector/client"
 	"go.opentelemetry.io/collector/component"
@@ -479,7 +481,7 @@ func authUnaryServerInterceptor(ctx context.Context, req any, _ *grpc.UnaryServe
 
 	ctx, err := server.Authenticate(ctx, headers)
 	if err != nil {
-		return nil, err
+		return nil, status.Error(codes.Unauthenticated, err.Error())
 	}
 
 	return handler(ctx, req)
@@ -494,7 +496,7 @@ func authStreamServerInterceptor(srv any, stream grpc.ServerStream, _ *grpc.Stre
 
 	ctx, err := server.Authenticate(ctx, headers)
 	if err != nil {
-		return err
+		return status.Error(codes.Unauthenticated, err.Error())
 	}
 
 	return handler(srv, wrapServerStream(ctx, stream))


Test patch:
diff --git a/config/configgrpc/configgrpc_test.go b/config/configgrpc/configgrpc_test.go
index 0b2b1cf4cd2..1769733dea6 100644
--- a/config/configgrpc/configgrpc_test.go
+++ b/config/configgrpc/configgrpc_test.go
@@ -19,8 +19,10 @@ import (
 	"go.uber.org/zap/zaptest/observer"
 	"google.golang.org/grpc"
 	"google.golang.org/grpc/balancer"
+	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/metadata"
 	"google.golang.org/grpc/peer"
+	"google.golang.org/grpc/status"
 
 	"go.opentelemetry.io/collector/client"
 	"go.opentelemetry.io/collector/component"
@@ -1022,7 +1024,8 @@ func TestDefaultUnaryInterceptorAuthFailure(t *testing.T) {
 
 	// verify
 	assert.Nil(t, res)
-	assert.Equal(t, expectedErr, err)
+	assert.ErrorContains(t, err, expectedErr.Error())
+	assert.Equal(t, codes.Unauthenticated, status.Code(err))
 	assert.True(t, authCalled)
 }
 
@@ -1098,7 +1101,8 @@ func TestDefaultStreamInterceptorAuthFailure(t *testing.T) {
 	err := authStreamServerInterceptor(nil, streamServer, &grpc.StreamServerInfo{}, handler, auth.NewServer(auth.WithServerAuthenticate(authFunc)))
 
 	// verify
-	assert.Equal(t, expectedErr, err)
+	assert.ErrorContains(t, err, expectedErr.Error()) // unfortunately, grpc errors don't wrap the original ones
+	assert.Equal(t, codes.Unauthenticated, status.Code(err))
 	assert.True(t, authCalled)
 }