From 87cf677d1dffb179ce9ad24315e5f36cedf989eb Mon Sep 17 00:00:00 2001 From: "Timur Kh." Date: Wed, 17 Dec 2025 12:31:40 +0300 Subject: [PATCH] use auth service for validating user in competitions service --- internal/competition/config/config.go | 4 +- internal/competition/middleware/auth.go | 67 +++++++++++++++++++++++++ internal/competition/server/server.go | 23 +++++++++ 3 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 internal/competition/middleware/auth.go diff --git a/internal/competition/config/config.go b/internal/competition/config/config.go index 3748ffd..6ef7be1 100644 --- a/internal/competition/config/config.go +++ b/internal/competition/config/config.go @@ -20,7 +20,7 @@ type Config struct { DBUser string DBPassword string DBName string - JWTSecret string + AuthSvcAddr string RedisAddr string RedisPassword string RedisDB int @@ -40,7 +40,7 @@ func Load() (*Config, error) { DBUser: getEnv("POSTGRES_USERNAME", "postgres"), DBPassword: getEnv("POSTGRES_PASSWORD", "postgres"), DBName: getEnv("POSTGRES_DATABASE", "postgres"), - JWTSecret: getEnv("JWT_SECRET", "your-secret-key-change-in-production"), + AuthSvcAddr: getEnv("AUTH_SVC_ADDR", "localhost:50051"), RedisAddr: getEnv("REDIS_ADDR", "localhost:6379"), RedisPassword: getEnv("REDIS_PASSWORD", ""), RedisDB: mustGetInt("REDIS_DB", 0), diff --git a/internal/competition/middleware/auth.go b/internal/competition/middleware/auth.go new file mode 100644 index 0000000..148f462 --- /dev/null +++ b/internal/competition/middleware/auth.go @@ -0,0 +1,67 @@ +package middleware + +import ( + "context" + "strings" + + authpb "datarush/pkg/api/auth" + + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" +) + +type contextKey string + +const ( + UserIDKey contextKey = "user_id" + authHeader = "authorization" + bearerScheme = "bearer" +) + +type AuthInterceptor struct { + authClient authpb.AuthServiceClient +} + +func NewAuthInterceptor(authClient authpb.AuthServiceClient) *AuthInterceptor { + return &AuthInterceptor{authClient: authClient} +} + +func (i *AuthInterceptor) Unary() 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.Error(codes.Unauthenticated, "metadata is not provided") + } + + authHeaders := md.Get(authHeader) + if len(authHeaders) == 0 { + return nil, status.Error(codes.Unauthenticated, "authorization token is not provided") + } + + header := authHeaders[0] + parts := strings.Split(header, " ") + if len(parts) != 2 || !strings.EqualFold(parts[0], bearerScheme) { + return nil, status.Errorf(codes.Unauthenticated, "invalid authorization header format") + } + + token := parts[1] + + validateResp, err := i.authClient.ValidateToken(ctx, &authpb.ValidateTokenRequest{ + Token: token, + }) + if err != nil { + return nil, status.Errorf(codes.Unauthenticated, "failed to validate token: %v", err) + } + + newCtx := context.WithValue(ctx, UserIDKey, validateResp.GetUserId()) + + return handler(newCtx, req) + } +} \ No newline at end of file diff --git a/internal/competition/server/server.go b/internal/competition/server/server.go index a63c278..4e0e82f 100644 --- a/internal/competition/server/server.go +++ b/internal/competition/server/server.go @@ -8,14 +8,17 @@ import ( "datarush/internal/competition/config" grpcHandlers "datarush/internal/competition/handler/grpc" + "datarush/internal/competition/middleware" "datarush/internal/competition/repository/postgres" "datarush/internal/competition/service" + authpb "datarush/pkg/api/auth" pb "datarush/pkg/api/competition" "github.com/jmoiron/sqlx" _ "github.com/lib/pq" "github.com/redis/go-redis/v9" "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/reflection" ) @@ -30,6 +33,7 @@ type Server struct { config *config.Config db *sqlx.DB redisClient *redis.Client + authConn *grpc.ClientConn } func New(cfg *config.Config) *Server { @@ -51,6 +55,12 @@ func (s *Server) Start() error { DB: s.config.RedisDB, }) + authConn, err := grpc.Dial(s.config.AuthSvcAddr, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + return fmt.Errorf("failed to connect to auth service: %w", err) + } + s.authConn = authConn + if err := s.registerGRPCServices(); err != nil { return fmt.Errorf("failed to register gRPC services: %w", err) } @@ -71,6 +81,13 @@ func (s *Server) Start() error { } func (s *Server) registerGRPCServices() error { + authClient := authpb.NewAuthServiceClient(s.authConn) + authInterceptor := middleware.NewAuthInterceptor(authClient) + + s.grpcServer = grpc.NewServer( + grpc.UnaryInterceptor(authInterceptor.Unary()), + ) + compRepo := postgres.NewCompetitionRepository(s.db, s.redisClient, s.config.CacheEnabled) compService := service.NewCompetitionService(compRepo) compHandler := grpcHandlers.NewCompetitionHandler(compService) @@ -91,6 +108,12 @@ func (s *Server) Stop() { s.grpcServer.GracefulStop() } + if s.authConn != nil { + if err := s.authConn.Close(); err != nil { + log.Printf("failed to close auth service connection: %v", err) + } + } + if s.db != nil { if err := s.db.Close(); err != nil { log.Printf("failed to close database: %v", err)