mirror of
https://github.com/stefanprodan/podinfo.git
synced 2026-08-23 22:26:29 +00:00
Added the token Validate api and updated the test for it
This commit is contained in:
+57
-3
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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{},
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user