@@ -28,6 +28,54 @@ func requiresAuthentication(fullMethod string) bool {
2828 return false
2929}
3030
31+ // validateCredentials extracts and validates credentials from the context metadata
32+ func validateCredentials (ctx context.Context , fullMethod , username , password string ) error {
33+ // Extract metadata from context
34+ md , ok := metadata .FromIncomingContext (ctx )
35+ if ! ok {
36+ slog .WarnContext (ctx , "Missing metadata in request" , "method" , fullMethod )
37+ return status .Error (codes .Unauthenticated , "missing metadata" )
38+ }
39+
40+ // Check for authorization header
41+ authHeaders := md .Get ("authorization" )
42+ if len (authHeaders ) == 0 {
43+ slog .WarnContext (ctx , "Missing authorization header" , "method" , fullMethod )
44+ return status .Error (codes .Unauthenticated , "missing authorization header" )
45+ }
46+
47+ // Parse Basic Auth header
48+ auth := authHeaders [0 ]
49+ if ! strings .HasPrefix (auth , "Basic " ) {
50+ slog .WarnContext (ctx , "Invalid authorization header format" , "method" , fullMethod )
51+ return status .Error (codes .Unauthenticated , "invalid authorization header" )
52+ }
53+
54+ // Decode base64 credentials
55+ payload , err := base64 .StdEncoding .DecodeString (auth [6 :])
56+ if err != nil {
57+ slog .WarnContext (ctx , "Failed to decode authorization header" , "method" , fullMethod , "error" , err )
58+ return status .Error (codes .Unauthenticated , "invalid authorization header" )
59+ }
60+
61+ // Split username:password
62+ pair := strings .SplitN (string (payload ), ":" , 2 )
63+ if len (pair ) != 2 {
64+ slog .WarnContext (ctx , "Invalid credentials format" , "method" , fullMethod )
65+ return status .Error (codes .Unauthenticated , "invalid credentials format" )
66+ }
67+
68+ // Validate credentials
69+ if pair [0 ] != username || pair [1 ] != password {
70+ slog .WarnContext (ctx , "Invalid credentials" , "method" , fullMethod , "username" , pair [0 ])
71+ return status .Error (codes .Unauthenticated , "invalid credentials" )
72+ }
73+
74+ // Credentials are valid
75+ slog .DebugContext (ctx , "Authentication successful" , "method" , fullMethod , "username" , pair [0 ])
76+ return nil
77+ }
78+
3179// SelectiveInterceptor creates a gRPC unary server interceptor that only applies
3280// authentication to specific services (Feature and Workload)
3381func SelectiveInterceptor (enabled bool , username , password string ) grpc.UnaryServerInterceptor {
@@ -48,49 +96,42 @@ func SelectiveInterceptor(enabled bool, username, password string) grpc.UnarySer
4896 return handler (ctx , req )
4997 }
5098
51- // Extract metadata from context
52- md , ok := metadata .FromIncomingContext (ctx )
53- if ! ok {
54- slog .WarnContext (ctx , "Missing metadata in request" , "method" , info .FullMethod )
55- return nil , status .Error (codes .Unauthenticated , "missing metadata" )
56- }
57-
58- // Check for authorization header
59- authHeaders := md .Get ("authorization" )
60- if len (authHeaders ) == 0 {
61- slog .WarnContext (ctx , "Missing authorization header" , "method" , info .FullMethod )
62- return nil , status .Error (codes .Unauthenticated , "missing authorization header" )
99+ // Validate credentials
100+ if err := validateCredentials (ctx , info .FullMethod , username , password ); err != nil {
101+ return nil , err
63102 }
64103
65- // Parse Basic Auth header
66- auth := authHeaders [0 ]
67- if ! strings .HasPrefix (auth , "Basic " ) {
68- slog .WarnContext (ctx , "Invalid authorization header format" , "method" , info .FullMethod )
69- return nil , status .Error (codes .Unauthenticated , "invalid authorization header" )
70- }
104+ // Credentials are valid, proceed with the request
105+ return handler (ctx , req )
106+ }
107+ }
71108
72- // Decode base64 credentials
73- payload , err := base64 .StdEncoding .DecodeString (auth [6 :])
74- if err != nil {
75- slog .WarnContext (ctx , "Failed to decode authorization header" , "method" , info .FullMethod , "error" , err )
76- return nil , status .Error (codes .Unauthenticated , "invalid authorization header" )
109+ // SelectiveStreamInterceptor creates a gRPC stream server interceptor that only applies
110+ // authentication to specific services (Feature and Workload)
111+ func SelectiveStreamInterceptor (enabled bool , username , password string ) grpc.StreamServerInterceptor {
112+ return func (
113+ srv interface {},
114+ ss grpc.ServerStream ,
115+ info * grpc.StreamServerInfo ,
116+ handler grpc.StreamHandler ,
117+ ) error {
118+ // If authentication is not enabled, allow all requests
119+ if ! enabled {
120+ return handler (srv , ss )
77121 }
78122
79- // Split username:password
80- pair := strings .SplitN (string (payload ), ":" , 2 )
81- if len (pair ) != 2 {
82- slog .WarnContext (ctx , "Invalid credentials format" , "method" , info .FullMethod )
83- return nil , status .Error (codes .Unauthenticated , "invalid credentials format" )
123+ // Only authenticate protected services (Feature and Workload)
124+ // Allow Health and Meta services to pass through without authentication
125+ if ! requiresAuthentication (info .FullMethod ) {
126+ return handler (srv , ss )
84127 }
85128
86129 // Validate credentials
87- if pair [0 ] != username || pair [1 ] != password {
88- slog .WarnContext (ctx , "Invalid credentials" , "method" , info .FullMethod , "username" , pair [0 ])
89- return nil , status .Error (codes .Unauthenticated , "invalid credentials" )
130+ if err := validateCredentials (ss .Context (), info .FullMethod , username , password ); err != nil {
131+ return err
90132 }
91133
92134 // Credentials are valid, proceed with the request
93- slog .DebugContext (ctx , "Authentication successful" , "method" , info .FullMethod , "username" , pair [0 ])
94- return handler (ctx , req )
135+ return handler (srv , ss )
95136 }
96137}
0 commit comments