// Package grpc provides gRPC client implementations for service clients. package grpc import ( "context" "fmt" authv1 "git.dcentral.systems/toolz/goplt/api/proto/generated/auth/v1" "git.dcentral.systems/toolz/goplt/pkg/registry" "git.dcentral.systems/toolz/goplt/pkg/services" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" ) // AuthClient implements AuthServiceClient using gRPC. type AuthClient struct { registry registry.ServiceRegistry conn *grpc.ClientConn client authv1.AuthServiceClient } // NewAuthClient creates a new gRPC client for the Auth Service. func NewAuthClient(reg registry.ServiceRegistry) (services.AuthServiceClient, error) { client := &AuthClient{ registry: reg, } return client, nil } // connect connects to the Auth Service. func (c *AuthClient) connect(ctx context.Context) error { if c.conn != nil { return nil } instances, err := c.registry.Discover(ctx, "auth-service") if err != nil { return fmt.Errorf("failed to discover auth service: %w", err) } if len(instances) == 0 { return fmt.Errorf("no instances found for auth-service") } instance := instances[0] address := fmt.Sprintf("%s:%d", instance.Address, instance.Port) conn, err := grpc.NewClient(address, grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { return fmt.Errorf("failed to connect to auth-service at %s: %w", address, err) } c.conn = conn c.client = authv1.NewAuthServiceClient(conn) return nil } // Login authenticates a user and returns access and refresh tokens. func (c *AuthClient) Login(ctx context.Context, email, password string) (*services.TokenResponse, error) { if err := c.connect(ctx); err != nil { return nil, err } resp, err := c.client.Login(ctx, &authv1.LoginRequest{ Email: email, Password: password, }) if err != nil { return nil, fmt.Errorf("login failed: %w", err) } return &services.TokenResponse{ AccessToken: resp.AccessToken, RefreshToken: resp.RefreshToken, ExpiresIn: resp.ExpiresIn, TokenType: resp.TokenType, }, nil } // RefreshToken refreshes an access token using a refresh token. func (c *AuthClient) RefreshToken(ctx context.Context, refreshToken string) (*services.TokenResponse, error) { if err := c.connect(ctx); err != nil { return nil, err } resp, err := c.client.RefreshToken(ctx, &authv1.RefreshTokenRequest{ RefreshToken: refreshToken, }) if err != nil { return nil, fmt.Errorf("refresh token failed: %w", err) } return &services.TokenResponse{ AccessToken: resp.AccessToken, RefreshToken: resp.RefreshToken, ExpiresIn: resp.ExpiresIn, TokenType: resp.TokenType, }, nil } // ValidateToken validates a JWT token and returns the token claims. func (c *AuthClient) ValidateToken(ctx context.Context, token string) (*services.TokenClaims, error) { if err := c.connect(ctx); err != nil { return nil, err } resp, err := c.client.ValidateToken(ctx, &authv1.ValidateTokenRequest{ Token: token, }) if err != nil { return nil, fmt.Errorf("validate token failed: %w", err) } return &services.TokenClaims{ UserID: resp.UserId, Email: resp.Email, Roles: resp.Roles, ExpiresAt: resp.ExpiresAt, }, nil } // Logout invalidates a refresh token. func (c *AuthClient) Logout(ctx context.Context, refreshToken string) error { if err := c.connect(ctx); err != nil { return err } _, err := c.client.Logout(ctx, &authv1.LogoutRequest{ RefreshToken: refreshToken, }) if err != nil { return fmt.Errorf("logout failed: %w", err) } return nil }