Files
goplt/internal/client/grpc/auth_client.go
0x1d b1b895e818 feat(epic2): Implement core authentication and authorization services
- Implement Audit Service (2.5)
  - gRPC server with Record and Query operations
  - Database persistence with audit schema
  - Service registry integration
  - Entry point: cmd/audit-service

- Implement Identity Service (2.2)
  - User CRUD operations
  - Password hashing with argon2id
  - Email verification and password reset flows
  - Entry point: cmd/identity-service
  - Fix package naming conflicts in user_service.go

- Implement Auth Service (2.1)
  - JWT token generation and validation
  - Login, RefreshToken, ValidateToken, Logout RPCs
  - Integration with Identity Service
  - Entry point: cmd/auth-service
  - Note: RefreshToken entity needs Ent generation

- Implement Authz Service (2.3, 2.4)
  - Permission checking and authorization
  - User roles and permissions retrieval
  - RBAC-based authorization
  - Entry point: cmd/authz-service

- Implement gRPC clients for all services
  - Auth, Identity, Authz, and Audit clients
  - Service discovery integration
  - Full gRPC communication

- Add service configurations to config/default.yaml
- Create SUMMARY.md with implementation details and testing instructions
- Fix compilation errors in Identity Service (password package conflicts)
- All services build successfully and tests pass
2025-11-06 20:07:20 +01:00

137 lines
3.5 KiB
Go

// 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
}