Skip to content

Commit

Permalink
feat: add jwt authorization and logging for error
Browse files Browse the repository at this point in the history
  • Loading branch information
inchori committed Nov 27, 2023
1 parent d908dc7 commit 066d700
Show file tree
Hide file tree
Showing 9 changed files with 225 additions and 51 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@ Build a Simple Blog Service with gRPC and goFiber

### ETC
- [X] GitHub Actions CI
- [ ] Error Handler
- [X] Error Handle with logging
- [ ] Swagger Docs
3 changes: 2 additions & 1 deletion handler/auth_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ package handler

import (
"context"
"github.com/gofiber/fiber/v2"
"grpc_identity/dto"
"grpc_identity/middleware"
"grpc_identity/service"
"grpc_identity/utils"
"strconv"

"github.com/gofiber/fiber/v2"
)

func NewLoginHandler(app fiber.Router, ctx context.Context, userService service.IUserService) {
Expand Down
2 changes: 1 addition & 1 deletion handler/post_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (

func NewPostHandler(app fiber.Router, ctx context.Context, postService service.IPostService,
userService service.IUserService, protected fiber.Handler) {
app.Post("", middleware.Protected(), CreatePost(ctx, postService, userService))
app.Post("", protected, CreatePost(ctx, postService, userService))
app.Get("/:id", GetPostByID(ctx, postService))
app.Get("/user/:userId", protected, GetPostByUserID(ctx, postService, userService))
app.Delete("/:id", protected, DeleteByID(ctx, postService, userService))
Expand Down
1 change: 1 addition & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ func main() {
}

methods := []string{
"proto.v1beta1.post.Post/CreatePost",
"proto.v1beta1.post.Post/UpdatePost",
"proto.v1beta1.post.Post/DeletePost",
"proto.v1beta1.user.Post/GetPostByUser",
Expand Down
11 changes: 8 additions & 3 deletions server/auth_grpc_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ package server

import (
"context"
"fmt"
"grpc_identity/middleware"
"grpc_identity/pb/v1beta1/auth"
"grpc_identity/service"
"grpc_identity/utils"
"strconv"

"github.com/sirupsen/logrus"
"google.golang.org/grpc"
)

Expand All @@ -25,16 +27,19 @@ func RegisterAuthService(userService service.IUserService, svr *grpc.Server) {
func (a *AuthGRPCServiceServer) Login(ctx context.Context, req *auth.LoginRequest) (*auth.LoginResponse, error) {
userByEmail, err := a.userService.GetUserByEmail(ctx, req.Email)
if err != nil {
return nil, err
logrus.Errorf("failed to get user by email: %v", err)
return nil, fmt.Errorf("failed to get user by email: %v", err)
}

if !utils.CheckPasswordHash(req.Password, userByEmail.Password) {
return nil, err
logrus.Errorf("invalid password")
return nil, fmt.Errorf("invalid password")
}

jwtToken, err := middleware.CreateAccessToken(strconv.Itoa(userByEmail.ID))
if err != nil {
return nil, err
logrus.Errorf("failed to create access jwt token: %v", err)
return nil, fmt.Errorf("failed to create access jwt token: %v", err)
}

return &auth.LoginResponse{Token: jwtToken}, nil
Expand Down
11 changes: 6 additions & 5 deletions server/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ package server

import (
"context"
"fmt"
"grpc_identity/pb/v1beta1/auth"
"grpc_identity/pb/v1beta1/post"
"grpc_identity/pb/v1beta1/user"
"log"

"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"github.com/sirupsen/logrus"
Expand All @@ -23,24 +23,25 @@ func GatewayServer(grpcListenAddr string) (*runtime.ServeMux, error) {
conn, err := grpc.DialContext(ctx, grpcListenAddr, opts...)
if err != nil {
logrus.Fatalf("failed to dial gRPC: %v", err)
return nil, err
return nil, fmt.Errorf("failed to dial gRPC: %v", err)
}

err = auth.RegisterAuthHandler(ctx, mux, conn)
if err != nil {
logrus.Fatalf("failed to register auth handler gateway: %v", err)
return nil, err
return nil, fmt.Errorf("failed to register auth handler gateway: %v\", err")

Check failure on line 32 in server/gateway.go

View workflow job for this annotation

GitHub Actions / golangci-lint

printf: fmt.Errorf format %v reads arg #1, but call has 0 args (govet)

Check failure on line 32 in server/gateway.go

View workflow job for this annotation

GitHub Actions / Build and Test

fmt.Errorf format %v reads arg #1, but call has 0 args
}

err = user.RegisterUserHandler(ctx, mux, conn)
if err != nil {
logrus.Fatalf("failed to register user handler gateway: %v", err)
return nil, err
return nil, fmt.Errorf("failed to register user handler gateway: %v", err)
}

err = post.RegisterPostHandler(ctx, mux, conn)
if err != nil {
log.Fatal(err)
logrus.Fatalf("failed to register post handler gateway: %v", err)
return nil, fmt.Errorf("failed to register post handler gateway: %v", err)
}

return mux, nil
Expand Down
41 changes: 33 additions & 8 deletions server/interceptor/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ package interceptor

import (
"context"
"fmt"

"github.com/golang-jwt/jwt/v5"
grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/auth"
"github.com/sirupsen/logrus"
"google.golang.org/grpc"
"log"
)

type JWTInterceptor struct {
Expand Down Expand Up @@ -38,29 +40,52 @@ func (ic *JWTInterceptor) Interceptor(ctx context.Context, req any, info *grpc.U
}
jwtTokenStr, err := grpc_auth.AuthFromMD(ctx, "Bearer")
if err != nil {
log.Fatalf("error jwt from header: %v", err)
logrus.Errorf("error jwt from header: %v", err)
return ctx, nil
}

token, err := jwt.Parse(jwtTokenStr, func(token *jwt.Token) (interface{}, error) {
return []byte("secret"), nil
})
if err != nil {
log.Fatalf("error jwt parsing: %v", err)
return nil, err
logrus.Errorf("error jwt parsing: %v", err)
return nil, fmt.Errorf("error jwt parsing: %v", err)
}

sub, err := token.Claims.GetSubject()
if err != nil {
log.Fatalf("invalid sub: %v", err)
return nil, err
logrus.Errorf("invalid sub: %v", err)
return nil, fmt.Errorf("invalid sub: %v", err)
}

if !token.Valid {
log.Fatalf("invalid jwt: %v", err)
return nil, err
logrus.Errorf("invalid jwt token: %v", err)
return nil, fmt.Errorf("invalid jwt token: %v", err)
}

newCtx := context.WithValue(ctx, ContextKeyAuthenticated{}, sub)
return newCtx, nil
}

func ExtractTokenFromMetadata(jwtToken string) (string, error) {
token, err := jwt.Parse(jwtToken, func(token *jwt.Token) (interface{}, error) {
return []byte("secret"), nil
})
if err != nil {
logrus.Errorf("error jwt parsing: %v", err)
return "", fmt.Errorf("error jwt parsing: %v", err)
}

sub, err := token.Claims.GetSubject()
if err != nil {
logrus.Errorf("invalid sub from claims: %v", err)
return "", fmt.Errorf("invalid sub from claims: %v", err)
}

if !token.Valid {
logrus.Errorf("invalid jwt token: %v", err)
return "", fmt.Errorf("invalid jwt token: %v", err)
}

return sub, nil
}
121 changes: 112 additions & 9 deletions server/post_grpc_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,15 @@ package server

import (
"context"
"google.golang.org/grpc"
"fmt"
"grpc_identity/pb/v1beta1/post"
"grpc_identity/server/interceptor"
"grpc_identity/service"
"strconv"

grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/auth"
"github.com/sirupsen/logrus"
"google.golang.org/grpc"
)

type PostGRPCServiceServer struct {
Expand All @@ -21,14 +27,35 @@ func RegisterPostService(postService service.IPostService, userService service.I
}

func (p *PostGRPCServiceServer) CreatePost(ctx context.Context, req *post.CreatePostRequest) (*post.CreatePostResponse, error) {
userByID, err := p.userService.GetUserByID(ctx, int(req.UserId))
jwtToken, err := grpc_auth.AuthFromMD(ctx, "Bearer")
if err != nil {
logrus.Errorf("failed to get jwt token: %v", err)
return nil, fmt.Errorf("failed to get jwt token: %v", err)
}

tokenClaimsID, err := interceptor.ExtractTokenFromMetadata(jwtToken)
if err != nil {
return nil, err
logrus.Errorf("failed to extract token from metadata: %v", err)
return nil, fmt.Errorf("failed to extract token from metadata: %v", err)
}

tokenID, _ := strconv.Atoi(tokenClaimsID)
fmt.Println(tokenID)
userByID, err := p.userService.GetUserByID(ctx, tokenID)
if err != nil {
logrus.Errorf("failed to get user: %v", err)
return nil, fmt.Errorf("failed to get user: %v", err)
}

if userByID.ID != tokenID {
logrus.Errorf("unauthorized")
return nil, fmt.Errorf("unauthorzied")
}

createPost, err := p.postService.CreatePost(ctx, req.Title, req.Content, userByID)
if err != nil {
return nil, err
logrus.Errorf("failed to create post: %v", err)
return nil, fmt.Errorf("failed to create post: %v", err)
}

postRes := &post.PostMessage{
Expand All @@ -43,7 +70,8 @@ func (p *PostGRPCServiceServer) CreatePost(ctx context.Context, req *post.Create
func (p *PostGRPCServiceServer) GetPost(ctx context.Context, req *post.GetPostByIDRequest) (*post.GetPostByIDResponse, error) {
postByID, err := p.postService.GetPostByID(ctx, int(req.Id))
if err != nil {
return nil, err
logrus.Errorf("failed to get post by ID: %v", err)
return nil, fmt.Errorf("failed to get post by ID: %v", err)
}

postRes := &post.PostMessage{
Expand All @@ -56,9 +84,34 @@ func (p *PostGRPCServiceServer) GetPost(ctx context.Context, req *post.GetPostBy
}

func (p *PostGRPCServiceServer) GetPostByUser(ctx context.Context, req *post.GetPostByUserRequest) (*post.GetPostByUserResponse, error) {
jwtToken, err := grpc_auth.AuthFromMD(ctx, "Bearer")
if err != nil {
logrus.Errorf("failed to get jwt token: %v", err)
return nil, fmt.Errorf("failed to get jwt token: %v", err)
}

tokenClaimsID, err := interceptor.ExtractTokenFromMetadata(jwtToken)
if err != nil {
logrus.Errorf("failed to extract token from metadata: %v", err)
return nil, fmt.Errorf("failed to extract token from metadata: %v", err)
}

tokenID, _ := strconv.Atoi(tokenClaimsID)
userByID, err := p.userService.GetUserByID(ctx, tokenID)
if err != nil {
logrus.Errorf("failed to get user: %v", err)
return nil, fmt.Errorf("failed to get user: %v", err)
}

if userByID.ID != tokenID {
logrus.Errorf("unauthorized")
return nil, fmt.Errorf("unauthorzied")
}

postsByUserID, err := p.postService.GetPostByUserID(ctx, int(req.UserId))
if err != nil {
return nil, err
logrus.Errorf("failed to get post by user: %v", err)
return nil, fmt.Errorf("failed to get post by user: %v", err)
}

var postResponses []*post.PostMessage
Expand All @@ -75,18 +128,68 @@ func (p *PostGRPCServiceServer) GetPostByUser(ctx context.Context, req *post.Get
}

func (p *PostGRPCServiceServer) DeletePost(ctx context.Context, req *post.DeletePostRequest) (*post.DeletePostResponse, error) {
err := p.postService.DeleteByID(ctx, int(req.Id))
jwtToken, err := grpc_auth.AuthFromMD(ctx, "Bearer")
if err != nil {
return nil, err
logrus.Errorf("failed to get jwt token: %v", err)
return nil, fmt.Errorf("failed to get jwt token: %v", err)
}

tokenClaimsID, err := interceptor.ExtractTokenFromMetadata(jwtToken)
if err != nil {
logrus.Errorf("failed to extract token from metadata: %v", err)
return nil, fmt.Errorf("failed to extract token from metadata: %v", err)
}

tokenID, _ := strconv.Atoi(tokenClaimsID)
userByID, err := p.userService.GetUserByID(ctx, tokenID)
if err != nil {
logrus.Errorf("failed to get user by ID: %v", err)
return nil, fmt.Errorf("failed to get user by ID: %v", err)
}

if userByID.ID != tokenID {
logrus.Errorf("unauthorized")
return nil, fmt.Errorf("unauthorzied")
}

err = p.postService.DeleteByID(ctx, int(req.Id))
if err != nil {
logrus.Errorf("failed to delete by ID: %v", err)
return nil, fmt.Errorf("failed to delete by ID: %v", err)
}

return &post.DeletePostResponse{}, nil
}

func (p *PostGRPCServiceServer) UpdatePost(ctx context.Context, req *post.UpdatePostRequest) (*post.UpdatePostResponse, error) {
jwtToken, err := grpc_auth.AuthFromMD(ctx, "Bearer")
if err != nil {
logrus.Errorf("failed to get jwt token: %v", err)
return nil, fmt.Errorf("failed to get jwt token: %v", err)
}

tokenClaimsID, err := interceptor.ExtractTokenFromMetadata(jwtToken)
if err != nil {
logrus.Errorf("failed to extract token from metadata: %v", err)
return nil, fmt.Errorf("failed to extract token from metadata: %v", err)
}

tokenID, _ := strconv.Atoi(tokenClaimsID)
userByID, err := p.userService.GetUserByID(ctx, tokenID)
if err != nil {
logrus.Errorf("failed to get user: %v", err)
return nil, fmt.Errorf("failed to get user: %v", err)
}

if userByID.ID != tokenID {
logrus.Error("unauthorized")
return nil, fmt.Errorf("unauthorzied")
}

updatePost, err := p.postService.UpdatePost(ctx, req.Content, req.Title, int(req.Id))
if err != nil {
return nil, err
logrus.Errorf("failed to update post: %v", err)
return nil, fmt.Errorf("failed to update post: %v", err)
}

postMsg := &post.PostMessage{
Expand Down
Loading

0 comments on commit 066d700

Please sign in to comment.