// Package validator implements the home-side Telegram validator gRPC service: it // verifies Mini App initData and Login Widget data with the bot token's HMAC and // never calls the Bot API. The gateway calls it during the auth.telegram and // link.telegram edge operations. It holds the token only as the HMAC secret, so it // can run on the main host with no VPN and no Telegram egress (ARCHITECTURE.md §12). package validator import ( "context" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" telegramv1 "scrabble/pkg/proto/telegram/v1" "scrabble/platform/telegram/internal/initdata" "scrabble/platform/telegram/internal/loginwidget" ) // Server implements the validation subset of telegramv1.TelegramServer; the // delivery methods (Notify, SendToUser, SendToGameChannel) are intentionally // unimplemented here — those run on the remote bot over the bot-link. type Server struct { telegramv1.UnimplementedTelegramServer initValidator initdata.Validator widgetValidator loginwidget.Validator } // NewServer builds the validator from the two HMAC validators bound to the token. func NewServer(iv initdata.Validator, wv loginwidget.Validator) *Server { return &Server{initValidator: iv, widgetValidator: wv} } // ValidateInitData verifies Mini App launch data against the bot's token and // returns the user identity. func (s *Server) ValidateInitData(_ context.Context, req *telegramv1.ValidateInitDataRequest) (*telegramv1.ValidateInitDataResponse, error) { u, err := s.initValidator.Validate(req.GetInitData()) if err != nil { return nil, status.Error(codes.InvalidArgument, err.Error()) } return &telegramv1.ValidateInitDataResponse{ ExternalId: u.ExternalID, Username: u.Username, FirstName: u.FirstName, LanguageCode: u.LanguageCode, }, nil } // ValidateLoginWidget verifies Login Widget authorization data against the bot's // token and returns the user identity, for attaching a Telegram identity to an // existing account. func (s *Server) ValidateLoginWidget(_ context.Context, req *telegramv1.ValidateLoginWidgetRequest) (*telegramv1.ValidateLoginWidgetResponse, error) { u, err := s.widgetValidator.Validate(req.GetData()) if err != nil { return nil, status.Error(codes.InvalidArgument, err.Error()) } return &telegramv1.ValidateLoginWidgetResponse{ ExternalId: u.ExternalID, Username: u.Username, FirstName: u.FirstName, }, nil }