From 51a7c1753dbfa97bfb055dbe75c68f2b5430530d Mon Sep 17 00:00:00 2001 From: mohammadne Date: Sun, 12 Sep 2021 11:35:39 +0430 Subject: [PATCH] feat: populate token --- services/auth/internal/jwt/failures.go | 95 ------------------- services/auth/internal/jwt/token.go | 91 ++++++++++-------- services/auth/internal/network/grpc/server.go | 2 +- .../internal/network/rest_api/handlers.go | 6 +- services/auth/pkg/failures/jwt.go | 72 ++++++++++++++ services/auth/pkg/logger/field.go | 4 +- 6 files changed, 130 insertions(+), 140 deletions(-) delete mode 100644 services/auth/internal/jwt/failures.go create mode 100644 services/auth/pkg/failures/jwt.go diff --git a/services/auth/internal/jwt/failures.go b/services/auth/internal/jwt/failures.go deleted file mode 100644 index 893ffef..0000000 --- a/services/auth/internal/jwt/failures.go +++ /dev/null @@ -1,95 +0,0 @@ -package jwt - -import ( - "fmt" - "net/http" - - "github.com/mohammadne/bookman/auth/pkg/failures" -) - -type failure struct { - FailureMessage string `json:"message"` - FailureStatus int `json:"status"` - FailureCauses []string `json:"causes"` -} - -// ==============================================================> methods - -func (f *failure) Message() string { - return f.FailureMessage -} - -func (f *failure) Status() int { - return f.FailureStatus -} - -func (f *failure) Causes() []string { - return f.FailureCauses -} - -func (f *failure) Error() string { - return fmt.Sprintf( - "message: %s - status: %d - causes: %v", - f.FailureMessage, f.FailureStatus, f.FailureCauses, - ) -} - -// ==============================================================> constructors - -type Failure struct{} - -func (Failure) New(message string, status int, causes []string) failures.Failure { - return &failure{ - FailureMessage: message, - FailureStatus: status, - FailureCauses: causes, - } -} - -func (Failure) NewBadRequest(message string) failures.Failure { - return &failure{ - FailureMessage: message, - FailureStatus: http.StatusBadRequest, - } -} - -func (Failure) NewNotFound(message string) failures.Failure { - return &failure{ - FailureMessage: message, - FailureStatus: http.StatusNotFound, - } -} - -func (Failure) NewUnauthorized(message string) failures.Failure { - return &failure{ - FailureMessage: message, - FailureStatus: http.StatusUnauthorized, - } -} - -func (Failure) NewUnprocessableEntity(message string) failures.Failure { - return &failure{ - FailureMessage: message, - FailureStatus: http.StatusUnprocessableEntity, - } -} - -func (Failure) NewNotImplemented() failures.Failure { - return &failure{ - FailureMessage: "not implemented", - FailureStatus: http.StatusNotImplemented, - } -} - -func (Failure) NewInternalServer(message string, errors ...error) failures.Failure { - causes := make([]string, 0, len(errors)) - for index := 0; index < len(errors); index++ { - causes = append(causes, errors[index].Error()) - } - - return &failure{ - FailureMessage: message, - FailureStatus: http.StatusInternalServerError, - FailureCauses: causes, - } -} diff --git a/services/auth/internal/jwt/token.go b/services/auth/internal/jwt/token.go index 361d33f..947d172 100644 --- a/services/auth/internal/jwt/token.go +++ b/services/auth/internal/jwt/token.go @@ -25,7 +25,7 @@ const ( type Jwt interface { CreateJwt(context.Context, uint64) (*models.Jwt, failures.Failure) ExtractTokenMetadata(context.Context, string, TokenType) (*models.AccessDetails, failures.Failure) - TokenValid(context.Context, string, TokenType) failures.Failure + // TokenValid(context.Context, string, TokenType) failures.Failure } type jwt struct { @@ -38,8 +38,14 @@ func New(cfg *Config, lg logger.Logger, tr trace.Tracer) Jwt { return &jwt{config: cfg, logger: lg, tracer: tr} } +var ( + createTokenFailure = failures.Jwt{}.NewInternal("token creation failed, please try later") + invalidClaimsFailure = failures.Jwt{}.NewInternal("token metadata is invalid, please try later") + invalidTokenFailure = failures.Jwt{}.NewInvalid("invalid token given") +) + func (jwt *jwt) CreateJwt(ctx context.Context, userId uint64) (*models.Jwt, failures.Failure) { - ctx, span := jwt.tracer.Start(ctx, "jwt.create_jwt") + _, span := jwt.tracer.Start(ctx, "jwt.create_jwt") defer span.End() accessExpires := time.Duration(jwt.config.AccessExpires) * time.Hour @@ -58,18 +64,16 @@ func (jwt *jwt) CreateJwt(ctx context.Context, userId uint64) (*models.Jwt, fail accessErr := createToken(userId, jwt.config.AccessSecret, tokenDetail.AccessToken) if accessErr != nil { - failure := Failure{}.NewUnprocessableEntity("unprocessable access token") - jwt.logger.Error(failure.Message(), logger.Error(accessErr)) + jwt.logger.Error(createTokenFailure.Message(), logger.Error(accessErr)) span.RecordError(accessErr) - return nil, failure + return nil, createTokenFailure } refreshErr := createToken(userId, jwt.config.RefreshSecret, tokenDetail.RefreshToken) if refreshErr != nil { - failure := Failure{}.NewUnprocessableEntity("unprocessable refresh token") - jwt.logger.Error(failure.Message(), logger.Error(refreshErr)) + jwt.logger.Error(createTokenFailure.Message(), logger.Error(refreshErr)) span.RecordError(refreshErr) - return nil, failure + return nil, createTokenFailure } return tokenDetail, nil @@ -93,55 +97,60 @@ func createToken(userId uint64, secret string, token *models.Token) error { return nil } -// failureUnprocessableEntity -// failureUnautorized = failures.Network{}.NewUnauthorized("unauthorized") -// failureUnautorized func (jwt *jwt) ExtractTokenMetadata(ctx context.Context, tokenString string, tokenType TokenType) (*models.AccessDetails, failures.Failure) { - ctx, span := jwt.tracer.Start(ctx, "jwt.extract_token_metadata") + _, span := jwt.tracer.Start(ctx, "jwt.extract_token_metadata") defer span.End() - token, failure := jwt.verifyToken(tokenString, tokenType) - if failure != nil { - span.RecordError(failure) - return nil, failure + token, err := jwt.verifyToken(tokenString, tokenType) + if err != nil { + jwt.logger.Error(invalidTokenFailure.Message(), logger.Error(err)) + span.RecordError(err) + return nil, invalidTokenFailure } claims, ok := token.Claims.(jwtPkg.MapClaims) - if ok && token.Valid { - tokenUuid, ok := claims["token_uuid"].(string) - if !ok { - return nil, err - } + if !ok { + err = fmt.Errorf("invalid map claims: %v", token.Claims) + jwt.logger.Error(invalidClaimsFailure.Message(), logger.Error(err)) + span.RecordError(err) + return nil, invalidClaimsFailure + } - userIdStr := fmt.Sprintf("%.f", claims["user_id"]) - userId, err := strconv.ParseUint(userIdStr, 10, 64) - if err != nil { - span.RecordError(err) - return nil, err - } + tokenUuid, ok := claims["token_uuid"].(string) + if !ok { + err = fmt.Errorf("ivalid claims: %v", claims) + jwt.logger.Error(invalidTokenFailure.Message(), logger.Error(err)) + span.RecordError(err) + return nil, invalidTokenFailure + } - return &models.AccessDetails{TokenUuid: tokenUuid, UserId: userId}, nil + userIdStr := fmt.Sprintf("%.f", claims["user_id"]) + userId, err := strconv.ParseUint(userIdStr, 10, 64) + if err != nil { + err = fmt.Errorf("ivalid claims: %v, err:%v", claims, err) + jwt.logger.Error(invalidTokenFailure.Message(), logger.Error(err)) + span.RecordError(err) + return nil, invalidTokenFailure } - err = errors.New("invalid token") - span.RecordError(err) - return nil, err + return &models.AccessDetails{TokenUuid: tokenUuid, UserId: userId}, nil } func (jwt *jwt) TokenValid(ctx context.Context, tokenString string, tokenType TokenType) failures.Failure { - token, err := jwt.verifyToken(tokenString, tokenType) - if err != nil { - return err - } + _, span := jwt.tracer.Start(ctx, "jwt.token_valid") + defer span.End() - if !token.Valid { - return errors.New("invalid token") + _, err := jwt.verifyToken(tokenString, tokenType) + if err != nil { + jwt.logger.Error(invalidTokenFailure.Message(), logger.Error(err)) + span.RecordError(err) + return invalidTokenFailure } return nil } -func (jwt *jwt) verifyToken(tokenString string, tokenType TokenType) (*jwtPkg.Token, failures.Failure) { +func (jwt *jwt) verifyToken(tokenString string, tokenType TokenType) (*jwtPkg.Token, error) { var secret string if tokenType == Access { secret = jwt.config.AccessSecret @@ -154,6 +163,10 @@ func (jwt *jwt) verifyToken(tokenString string, tokenType TokenType) (*jwtPkg.To return nil, err } + if !token.Valid { + return nil, errors.New("invalid token") + } + return token, nil } @@ -162,7 +175,7 @@ func (jwt *jwt) checkSigningMethod(secret string) jwtPkg.Keyfunc { return func(token *jwtPkg.Token) (interface{}, error) { if _, ok := token.Method.(*jwtPkg.SigningMethodHMAC); !ok { s := token.Header["alg"] - jwt.logger.Error("unexpected signing method", logger.Unknown("token", s)) + jwt.logger.Error("unexpected signing method", logger.Any("token", s)) return nil, fmt.Errorf("unexpected signing method: %v", s) } diff --git a/services/auth/internal/network/grpc/server.go b/services/auth/internal/network/grpc/server.go index 4cb76b8..edbc09f 100644 --- a/services/auth/internal/network/grpc/server.go +++ b/services/auth/internal/network/grpc/server.go @@ -48,7 +48,7 @@ func (s *grpcServer) Serve(<-chan struct{}) { func (s *grpcServer) TokenMetadata(ctx context.Context, token *pb.TokenContract, ) (*pb.TokenMetadataResponse, error) { - accessDetails, err := s.jwt.ExtractTokenMetadata(token.Token, jwt.Access) + accessDetails, err := s.jwt.ExtractTokenMetadata(ctx, token.Token, jwt.Access) if err != nil { return nil, err } diff --git a/services/auth/internal/network/rest_api/handlers.go b/services/auth/internal/network/rest_api/handlers.go index 1aafa29..ff063af 100644 --- a/services/auth/internal/network/rest_api/handlers.go +++ b/services/auth/internal/network/rest_api/handlers.go @@ -77,7 +77,7 @@ func (handler restServer) signOut(c echo.Context) error { defer span.End() tokenString := extractToken(c.Request()) - accessDetails, failure := handler.jwt.ExtractTokenMetadata(tokenString, jwt.Access) + accessDetails, failure := handler.jwt.ExtractTokenMetadata(ctx, tokenString, jwt.Access) if failure != nil { span.RecordError(failure) return c.JSON(failure.Status(), failure) @@ -106,7 +106,7 @@ func (handler restServer) refreshToken(c echo.Context) error { return c.JSON(failureInvalidBody.Status(), failureInvalidBody) } - accessDetails, failure := handler.jwt.ExtractTokenMetadata(body.RefreshToken, jwt.Refresh) + accessDetails, failure := handler.jwt.ExtractTokenMetadata(ctx, body.RefreshToken, jwt.Refresh) if failure != nil { span.RecordError(failure) return c.JSON(failure.Status(), failure) @@ -128,7 +128,7 @@ func (handler restServer) refreshToken(c echo.Context) error { } func (handler restServer) generateTokens(ctx context.Context, userId uint64) (map[string]string, failures.Failure) { - jwt, failure := handler.jwt.CreateJwt(userId) + jwt, failure := handler.jwt.CreateJwt(ctx, userId) if failure != nil { return nil, failure } diff --git a/services/auth/pkg/failures/jwt.go b/services/auth/pkg/failures/jwt.go new file mode 100644 index 0000000..2a6dfb0 --- /dev/null +++ b/services/auth/pkg/failures/jwt.go @@ -0,0 +1,72 @@ +package failures + +import ( + "fmt" + "net/http" +) + +type jwtFailure struct { + FailureMessage string `json:"message"` + FailureStatus int `json:"status"` + FailureCauses []string `json:"causes"` +} + +// ==============================================================> methods + +func (f *jwtFailure) Message() string { + return f.FailureMessage +} + +func (f *jwtFailure) Status() int { + return f.FailureStatus +} + +func (f *jwtFailure) Causes() []string { + return f.FailureCauses +} + +func (f *jwtFailure) Error() string { + return fmt.Sprintf( + "message: %s - status: %d - causes: %v", + f.FailureMessage, f.FailureStatus, f.FailureCauses, + ) +} + +// ==============================================================> constructors + +type Jwt struct{} + +func (Jwt) New(message string, status int, causes []string) Failure { + return &jwtFailure{ + FailureMessage: message, + FailureStatus: status, + FailureCauses: causes, + } +} + +func (Jwt) NewUnprocessableToken(message string) Failure { + return &jwtFailure{ + FailureMessage: message, + FailureStatus: http.StatusUnprocessableEntity, + } +} + +func (Jwt) NewInvalid(message string) Failure { + return &jwtFailure{ + FailureMessage: message, + FailureStatus: http.StatusUnauthorized, + } +} + +func (Jwt) NewInternal(message string, errors ...error) Failure { + causes := make([]string, 0, len(errors)) + for index := 0; index < len(errors); index++ { + causes = append(causes, errors[index].Error()) + } + + return &jwtFailure{ + FailureMessage: message, + FailureStatus: http.StatusInternalServerError, + FailureCauses: causes, + } +} diff --git a/services/auth/pkg/logger/field.go b/services/auth/pkg/logger/field.go index e3db6c5..0c2320e 100644 --- a/services/auth/pkg/logger/field.go +++ b/services/auth/pkg/logger/field.go @@ -17,8 +17,8 @@ type Field struct { Type FieldType } -// Unknown constructs a field with the given key and value. -func Unknown(key string, val interface{}) Field { +// Any constructs a field with the given key and value. +func Any(key string, val interface{}) Field { return Field{Key: key, Value: val, Type: UnknownType} }