From 910883321444cdfc021a122740ea9c78808d7860 Mon Sep 17 00:00:00 2001 From: Prashant Dwivedi Date: Thu, 9 May 2024 12:56:54 +0530 Subject: [PATCH] Added the token Validate api and updated the test for it --- pkg/api/grpc/token.go | 60 +++++++++++++++++++++++++++-- pkg/api/grpc/token/token.pb.go | 40 +++++++++++++------ pkg/api/grpc/token/token.proto | 5 ++- pkg/api/grpc/token/token_grpc.pb.go | 60 +++++++++++++++++++++++------ pkg/api/grpc/token_test.go | 7 ++-- 5 files changed, 140 insertions(+), 32 deletions(-) diff --git a/pkg/api/grpc/token.go b/pkg/api/grpc/token.go index 838be29..b92f415 100644 --- a/pkg/api/grpc/token.go +++ b/pkg/api/grpc/token.go @@ -2,11 +2,14 @@ package grpc import ( "context" + "strings" "time" "github.com/golang-jwt/jwt/v4" - "go.uber.org/zap" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" pb "github.com/stefanprodan/podinfo/pkg/api/grpc/token" ) @@ -24,7 +27,7 @@ type jwtCustomClaims struct { // SayHello implements helloworld.GreeterServer -func (s *TokenServer) Token(ctx context.Context, req *pb.TokenRequest) (*pb.TokenResponse, error) { +func (s *TokenServer) TokenGenerate(ctx context.Context, req *pb.TokenRequest) (*pb.TokenResponse, error) { user := "anonymous" expiresAt := time.Now().Add(time.Minute * 1).Unix() @@ -38,7 +41,7 @@ func (s *TokenServer) Token(ctx context.Context, req *pb.TokenRequest) (*pb.Toke } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) - t, err := token.SignedString([]byte("secret")) + t, err := token.SignedString([]byte(s.config.JWTSecret)) if err != nil { s.logger.Error("Failed to generate token", zap.Error(err)) @@ -48,7 +51,58 @@ func (s *TokenServer) Token(ctx context.Context, req *pb.TokenRequest) (*pb.Toke var result = pb.TokenResponse{ Token: t, ExpiresAt: time.Unix(claims.StandardClaims.ExpiresAt, 0).String(), + Message: "Token generated successfully", } return &result, nil } + +// code to get the authorization token from the header of grpc request and validate it if it is expired or not +func (s *TokenServer) TokenValidate(ctx context.Context, req *pb.TokenRequest) (*pb.TokenResponse, error) { + md, ok := metadata.FromIncomingContext(ctx) + if !ok { + return nil, status.Errorf(codes.DataLoss, "UnaryEcho: failed to get metadata") + } + + // Retrieve the bearer token from the "authorization" key in metadata + authorization := md.Get("authorization") + + if len(authorization) == 0 { + return nil, status.Errorf(codes.Unauthenticated, "Authorization token not found in metadata") + } + + // Extract the token from the value + token := strings.TrimSpace(strings.TrimPrefix(authorization[0], "Bearer")) + + claims := jwtCustomClaims{} + + parsed_token, err := jwt.ParseWithClaims(token, &claims, func(parsed_token *jwt.Token) (interface{}, error) { + if _, ok := parsed_token.Method.(*jwt.SigningMethodHMAC); !ok { + return nil, status.Errorf(codes.Canceled, "invalid signing method") + } + return []byte(s.config.JWTSecret), nil + }) + if err != nil { + if strings.Contains(err.Error(), "token is expired") || strings.Contains(err.Error(), "signature is invalid") { + return &pb.TokenResponse{ + Message: err.Error(), + }, nil + } + return nil, status.Errorf(codes.Unauthenticated, "Unable to parse token") + + } + + if parsed_token.Valid { + if claims.StandardClaims.Issuer != "podinfo" { + return nil, status.Errorf(codes.OK, "Invalid issuer") + } else { + var result = pb.TokenResponse{ + Token: claims.Name, + ExpiresAt: time.Unix(claims.StandardClaims.ExpiresAt, 0).String(), + } + return &result, nil + } + } else { + return nil, status.Errorf(codes.Unauthenticated, "Unauthenticated") + } +} diff --git a/pkg/api/grpc/token/token.pb.go b/pkg/api/grpc/token/token.pb.go index d5b5d11..9633657 100644 --- a/pkg/api/grpc/token/token.pb.go +++ b/pkg/api/grpc/token/token.pb.go @@ -65,6 +65,7 @@ type TokenResponse struct { Token string `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"` ExpiresAt string `protobuf:"bytes,2,opt,name=expiresAt,proto3" json:"expiresAt,omitempty"` + Message string `protobuf:"bytes,3,opt,name=message,proto3" json:"message,omitempty"` } func (x *TokenResponse) Reset() { @@ -113,22 +114,35 @@ func (x *TokenResponse) GetExpiresAt() string { return "" } +func (x *TokenResponse) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + var File_token_token_proto protoreflect.FileDescriptor var file_token_token_proto_rawDesc = []byte{ 0x0a, 0x11, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x2f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x0e, 0x0a, 0x0c, 0x54, 0x6f, - 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x43, 0x0a, 0x0d, 0x54, 0x6f, + 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x5d, 0x0a, 0x0d, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x41, 0x74, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x41, 0x74, 0x32, - 0x44, 0x0a, 0x0c, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, - 0x34, 0x0a, 0x05, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x13, 0x2e, 0x74, 0x6f, 0x6b, 0x65, 0x6e, - 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, - 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x09, 0x5a, 0x07, 0x2e, 0x2f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x41, 0x74, 0x12, + 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x32, 0x8a, 0x01, 0x0a, 0x0c, 0x54, 0x6f, + 0x6b, 0x65, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x3c, 0x0a, 0x0d, 0x54, 0x6f, + 0x6b, 0x65, 0x6e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x12, 0x13, 0x2e, 0x74, 0x6f, + 0x6b, 0x65, 0x6e, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x14, 0x2e, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3c, 0x0a, 0x0d, 0x54, 0x6f, 0x6b, 0x65, + 0x6e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x12, 0x13, 0x2e, 0x74, 0x6f, 0x6b, 0x65, + 0x6e, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, + 0x2e, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x09, 0x5a, 0x07, 0x2e, 0x2f, 0x74, 0x6f, 0x6b, 0x65, + 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -149,10 +163,12 @@ var file_token_token_proto_goTypes = []interface{}{ (*TokenResponse)(nil), // 1: token.TokenResponse } var file_token_token_proto_depIdxs = []int32{ - 0, // 0: token.TokenService.Token:input_type -> token.TokenRequest - 1, // 1: token.TokenService.Token:output_type -> token.TokenResponse - 1, // [1:2] is the sub-list for method output_type - 0, // [0:1] is the sub-list for method input_type + 0, // 0: token.TokenService.TokenGenerate:input_type -> token.TokenRequest + 0, // 1: token.TokenService.TokenValidate:input_type -> token.TokenRequest + 1, // 2: token.TokenService.TokenGenerate:output_type -> token.TokenResponse + 1, // 3: token.TokenService.TokenValidate:output_type -> token.TokenResponse + 2, // [2:4] is the sub-list for method output_type + 0, // [0:2] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name diff --git a/pkg/api/grpc/token/token.proto b/pkg/api/grpc/token/token.proto index 6f87578..1e61fcc 100644 --- a/pkg/api/grpc/token/token.proto +++ b/pkg/api/grpc/token/token.proto @@ -7,13 +7,14 @@ package token; // The greeting service definition. service TokenService { - rpc Token (TokenRequest) returns (TokenResponse) {} + rpc TokenGenerate (TokenRequest) returns (TokenResponse) {} + rpc TokenValidate (TokenRequest) returns (TokenResponse) {} } - message TokenRequest {} message TokenResponse { string token = 1; string expiresAt = 2; + string message = 3; } \ No newline at end of file diff --git a/pkg/api/grpc/token/token_grpc.pb.go b/pkg/api/grpc/token/token_grpc.pb.go index 7e4f221..b3719ef 100644 --- a/pkg/api/grpc/token/token_grpc.pb.go +++ b/pkg/api/grpc/token/token_grpc.pb.go @@ -22,7 +22,8 @@ const _ = grpc.SupportPackageIsVersion7 // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type TokenServiceClient interface { - Token(ctx context.Context, in *TokenRequest, opts ...grpc.CallOption) (*TokenResponse, error) + TokenGenerate(ctx context.Context, in *TokenRequest, opts ...grpc.CallOption) (*TokenResponse, error) + TokenValidate(ctx context.Context, in *TokenRequest, opts ...grpc.CallOption) (*TokenResponse, error) } type tokenServiceClient struct { @@ -33,9 +34,18 @@ func NewTokenServiceClient(cc grpc.ClientConnInterface) TokenServiceClient { return &tokenServiceClient{cc} } -func (c *tokenServiceClient) Token(ctx context.Context, in *TokenRequest, opts ...grpc.CallOption) (*TokenResponse, error) { +func (c *tokenServiceClient) TokenGenerate(ctx context.Context, in *TokenRequest, opts ...grpc.CallOption) (*TokenResponse, error) { out := new(TokenResponse) - err := c.cc.Invoke(ctx, "/token.TokenService/Token", in, out, opts...) + err := c.cc.Invoke(ctx, "/token.TokenService/TokenGenerate", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *tokenServiceClient) TokenValidate(ctx context.Context, in *TokenRequest, opts ...grpc.CallOption) (*TokenResponse, error) { + out := new(TokenResponse) + err := c.cc.Invoke(ctx, "/token.TokenService/TokenValidate", in, out, opts...) if err != nil { return nil, err } @@ -46,7 +56,8 @@ func (c *tokenServiceClient) Token(ctx context.Context, in *TokenRequest, opts . // All implementations must embed UnimplementedTokenServiceServer // for forward compatibility type TokenServiceServer interface { - Token(context.Context, *TokenRequest) (*TokenResponse, error) + TokenGenerate(context.Context, *TokenRequest) (*TokenResponse, error) + TokenValidate(context.Context, *TokenRequest) (*TokenResponse, error) mustEmbedUnimplementedTokenServiceServer() } @@ -54,8 +65,11 @@ type TokenServiceServer interface { type UnimplementedTokenServiceServer struct { } -func (UnimplementedTokenServiceServer) Token(context.Context, *TokenRequest) (*TokenResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method Token not implemented") +func (UnimplementedTokenServiceServer) TokenGenerate(context.Context, *TokenRequest) (*TokenResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method TokenGenerate not implemented") +} +func (UnimplementedTokenServiceServer) TokenValidate(context.Context, *TokenRequest) (*TokenResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method TokenValidate not implemented") } func (UnimplementedTokenServiceServer) mustEmbedUnimplementedTokenServiceServer() {} @@ -70,20 +84,38 @@ func RegisterTokenServiceServer(s grpc.ServiceRegistrar, srv TokenServiceServer) s.RegisterService(&TokenService_ServiceDesc, srv) } -func _TokenService_Token_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { +func _TokenService_TokenGenerate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(TokenRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(TokenServiceServer).Token(ctx, in) + return srv.(TokenServiceServer).TokenGenerate(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/token.TokenService/Token", + FullMethod: "/token.TokenService/TokenGenerate", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(TokenServiceServer).Token(ctx, req.(*TokenRequest)) + return srv.(TokenServiceServer).TokenGenerate(ctx, req.(*TokenRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TokenService_TokenValidate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(TokenRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TokenServiceServer).TokenValidate(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/token.TokenService/TokenValidate", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TokenServiceServer).TokenValidate(ctx, req.(*TokenRequest)) } return interceptor(ctx, in, info, handler) } @@ -96,8 +128,12 @@ var TokenService_ServiceDesc = grpc.ServiceDesc{ HandlerType: (*TokenServiceServer)(nil), Methods: []grpc.MethodDesc{ { - MethodName: "Token", - Handler: _TokenService_Token_Handler, + MethodName: "TokenGenerate", + Handler: _TokenService_TokenGenerate_Handler, + }, + { + MethodName: "TokenValidate", + Handler: _TokenService_TokenValidate_Handler, }, }, Streams: []grpc.StreamDesc{}, diff --git a/pkg/api/grpc/token_test.go b/pkg/api/grpc/token_test.go index 30a6a39..69df3a8 100644 --- a/pkg/api/grpc/token_test.go +++ b/pkg/api/grpc/token_test.go @@ -25,8 +25,9 @@ func TestGrpcToken(t *testing.T) { t.Cleanup(func() { srv.Stop() }) - - token.RegisterTokenServiceServer(srv, &TokenServer{}) + config := &Config{} + config.JWTSecret = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" + token.RegisterTokenServiceServer(srv, &TokenServer{config: config}) go func() { if err := srv.Serve(lis); err != nil { @@ -51,7 +52,7 @@ func TestGrpcToken(t *testing.T) { } client := token.NewTokenServiceClient(conn) - res, err := client.Token(context.Background(), &token.TokenRequest{}) + res, err := client.TokenGenerate(context.Background(), &token.TokenRequest{}) // Check the status code is what we expect. if _, ok := status.FromError(err); !ok {