Skip to content

Commit

Permalink
make grpc auth service
Browse files Browse the repository at this point in the history
  • Loading branch information
Prrromanssss committed Apr 18, 2024
1 parent 4a1d6cd commit da9718a
Show file tree
Hide file tree
Showing 25 changed files with 1,120 additions and 40 deletions.
10 changes: 6 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
pull-rabbitmq:
docker pull rabbitmq:3-management

run-rabbitmq:
docker run -d --name my-rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:3-management

run-postgres:
docker run --name habr-pg-13.3 -p 5432:5432 -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=daee -d postgres:13.3
docker run --name habr-pg-13.3 -p 5432:5432 -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=daee -d postgres:13.3

generate:
cd ./backend/internal/protos && protoc -I proto proto/daee/daee.proto \
--go_out=./gen/go --go_opt=paths=source_relative \
--go-grpc_out=./gen/go --go-grpc_opt=paths=source_relative
9 changes: 8 additions & 1 deletion backend/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,22 @@ require github.com/lib/pq v1.10.9
require (
github.com/fatih/color v1.16.0
github.com/go-chi/chi/v5 v5.0.12
github.com/golang-jwt/jwt/v5 v5.2.1
github.com/ilyakaznacheev/cleanenv v1.5.0
github.com/streadway/amqp v1.1.0
golang.org/x/crypto v0.22.0
google.golang.org/grpc v1.63.2
google.golang.org/protobuf v1.33.0
)

require (
github.com/BurntSushi/toml v1.2.1 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
golang.org/x/sys v0.14.0 // indirect
golang.org/x/net v0.21.0 // indirect
golang.org/x/sys v0.19.0 // indirect
golang.org/x/text v0.14.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3 // indirect
)
20 changes: 18 additions & 2 deletions backend/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s=
github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/ilyakaznacheev/cleanenv v1.5.0 h1:0VNZXggJE2OYdXE87bfSSwGxeiGt9moSR2lOrsHHvr4=
github.com/ilyakaznacheev/cleanenv v1.5.0/go.mod h1:a5aDzaJrLCQZsazHol1w8InnDcOX0OColm64SlIi6gk=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
Expand All @@ -21,10 +25,22 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/streadway/amqp v1.1.0 h1:py12iX8XSyI7aN/3dUT8DFIDJazNJsVJdxNVEpnQTZM=
github.com/streadway/amqp v1.1.0/go.mod h1:WYSrTEYHOXHd0nwFeUXAe2G2hRnQT+deZJJf88uS9Bg=
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY=
google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
Expand Down
70 changes: 70 additions & 0 deletions backend/internal/app/grpc/app.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package grpcapp

import (
"fmt"
"log/slog"
"net"

authgrpc "github.com/Prrromanssss/DAEE-fullstack/internal/grpc/auth"
"google.golang.org/grpc"
)

type App struct {
log *slog.Logger
gRPCServer *grpc.Server
port int
}

// MustRun runs gRPC server and panics if any error occurs.
func (a *App) MustRun() {
if err := a.Run(); err != nil {
panic(err)
}
}

// New creates new gRPC server app.
func New(
log *slog.Logger,
authService authgrpc.Auth,
port int,
) *App {
gRPCServer := grpc.NewServer()

authgrpc.Register(gRPCServer, authService)

return &App{
log: log,
gRPCServer: gRPCServer,
port: port,
}
}

func (a *App) Run() error {
const op = "grpcapp.Run"

log := a.log.With(
slog.String("op", op),
slog.Int("port", a.port),
)

lis, err := net.Listen("tcp", fmt.Sprintf(":%d", a.port))
if err != nil {
return fmt.Errorf("%s: %w", op, err)
}
log.Info("gRPC server is running", slog.String("addr", lis.Addr().String()))

if err := a.gRPCServer.Serve(lis); err != nil {
return fmt.Errorf("%s: %w", op, err)
}
return nil
}

// Stop stops gRPC server.
func (a *App) Stop() {
const op = "grpcapp.Stop"

a.log.With(slog.String("op", op)).
Info("stopping gRPC server", slog.Int("port", a.port))

a.gRPCServer.GracefulStop()
}
3 changes: 2 additions & 1 deletion backend/internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@ type DatabaseInstance struct {
func MustLoad() *Config {
err := godotenv.Load("local.env")
if err != nil {
log.Fatalf("Can't parse env file: %v", err)
log.Fatalf("can't parse env file: %v", err)
}

configPath := os.Getenv("CONFIG_PATH")
if configPath == "" {
log.Fatal("CONFIG_PATH is not set")
Expand Down
99 changes: 99 additions & 0 deletions backend/internal/grpc/auth/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package auth

import (
"context"
"errors"

daeev1 "github.com/Prrromanssss/DAEE-fullstack/internal/protos/gen/go/daee"
"github.com/Prrromanssss/DAEE-fullstack/internal/services/auth"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)

type Auth interface {
Login(
ctx context.Context,
email string,
password string,
) (token string, err error)
RegisterNewUser(
ctx context.Context,
email string,
password string,
) (userID int64, err error)
}

type serverAPI struct {
daeev1.UnimplementedAuthServer
auth Auth
}

func Register(gRPC *grpc.Server, auth Auth) {
daeev1.RegisterAuthServer(gRPC, &serverAPI{auth: auth})
}

func (s *serverAPI) Login(
ctx context.Context,
req *daeev1.LoginRequest,
) (*daeev1.LoginResponse, error) {
if err := validateLogin(req); err != nil {
return nil, err
}

token, err := s.auth.Login(ctx, req.GetEmail(), req.GetPassword())
if err != nil {
if errors.Is(err, auth.ErrInvalidCredentials) {
return nil, status.Error(codes.InvalidArgument, "invalid email or password")
}
return nil, status.Error(codes.Internal, "internal error")
}

return &daeev1.LoginResponse{
Token: token,
}, nil
}

func (s *serverAPI) Register(
ctx context.Context,
req *daeev1.RegisterRequest,
) (*daeev1.RegisterResponse, error) {
if err := validateRegister(req); err != nil {
return nil, err
}

userID, err := s.auth.RegisterNewUser(ctx, req.GetEmail(), req.GetPassword())
if err != nil {
if errors.Is(err, auth.ErrUserExists) {
return nil, status.Error(codes.AlreadyExists, "user already exists")
}
return nil, status.Error(codes.Internal, "internal error")
}

return &daeev1.RegisterResponse{
UserId: userID,
}, nil
}

func validateLogin(req *daeev1.LoginRequest) error {
if req.GetEmail() == "" {
return status.Error(codes.InvalidArgument, "email is required")
}

if req.GetPassword() == "" {
return status.Error(codes.InvalidArgument, "password is required")
}

return nil
}

func validateRegister(req *daeev1.RegisterRequest) error {
if req.GetEmail() == "" {
return status.Error(codes.InvalidArgument, "email is required")
}

if req.GetPassword() == "" {
return status.Error(codes.InvalidArgument, "password is required")
}
return nil
}
36 changes: 36 additions & 0 deletions backend/internal/lib/jwt/jwt.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package jwt

import (
"log"
"os"
"time"

"github.com/Prrromanssss/DAEE-fullstack/internal/storage/postgres"
"github.com/golang-jwt/jwt/v5"
"github.com/joho/godotenv"
)

func NewToken(user postgres.User, duration time.Duration) (string, error) {
err := godotenv.Load("local.env")
if err != nil {
log.Fatalf("can't parse env file: %v", err)
}

jwtSecret := os.Getenv("JWT_SECRET")
if jwtSecret == "" {
log.Fatal("JWT_SECRET is not set")
}

token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"uid": user.UserID,
"email": user.Email,
"exp": time.Now().Add(duration).Unix(),
})

tokenString, err := token.SignedString([]byte(jwtSecret))
if err != nil {
return "", err
}

return tokenString, nil
}
Loading

0 comments on commit da9718a

Please sign in to comment.