Skip to content

Commit

Permalink
add get access token API
Browse files Browse the repository at this point in the history
  • Loading branch information
le-xuan-quynh committed May 3, 2022
1 parent 830703e commit 65f43a5
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 7 deletions.
23 changes: 23 additions & 0 deletions pkg/authorization/endpoints/endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type Set struct {
UpdatePasswordEndpoint endpoint.Endpoint
GetForgetPasswordCodeEndpoint endpoint.Endpoint
ResetPasswordEndpoint endpoint.Endpoint
GenerateAccessTokenEndpoint endpoint.Endpoint
}

func NewEndpointSet(svc authorization.Service,
Expand Down Expand Up @@ -80,6 +81,11 @@ func NewEndpointSet(svc authorization.Service,
resetPasswordEndpoint = middleware.RateLimitRequest(tb, logger)(resetPasswordEndpoint)
resetPasswordEndpoint = middleware.ValidateParamRequest(validator, logger)(resetPasswordEndpoint)

generateAccessTokenEndpoint := MakeGenerateAccessTokenEndpoint(svc)
generateAccessTokenEndpoint = middleware.RateLimitRequest(tb, logger)(generateAccessTokenEndpoint)
generateAccessTokenEndpoint = middleware.ValidateParamRequest(validator, logger)(generateAccessTokenEndpoint)
generateAccessTokenEndpoint = middleware.ValidateRefreshToken(auth, r, logger)(generateAccessTokenEndpoint)

return Set{
HealthCheckEndpoint: healthCheckEndpoint,
RegisterEndpoint: registerEndpoint,
Expand All @@ -92,6 +98,7 @@ func NewEndpointSet(svc authorization.Service,
UpdatePasswordEndpoint: updatePasswordEndpoint,
GetForgetPasswordCodeEndpoint: getForgetPasswordCodeEndpoint,
ResetPasswordEndpoint: resetPasswordEndpoint,
GenerateAccessTokenEndpoint: generateAccessTokenEndpoint,
}
}

Expand Down Expand Up @@ -285,3 +292,19 @@ func MakeCreateNewPasswordWithCodeEndpoint(svc authorization.Service) endpoint.E
return "successfully updated password.", nil
}
}

// MakeGenerateAccessTokenEndpoint returns an endpoint that invokes GenerateAccessToken on the service.
func MakeGenerateAccessTokenEndpoint(svc authorization.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req, ok := request.(authorization.GenerateAccessTokenRequest)
if !ok {
cusErr := utils.NewErrorResponse(utils.BadRequest)
return nil, cusErr
}
token, err := svc.GenerateAccessToken(ctx, &req)
if err != nil {
return nil, err
}
return token, nil
}
}
15 changes: 8 additions & 7 deletions pkg/authorization/middleware/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,47 +20,48 @@ type UserIDKey struct{}
func ValidateRefreshToken(auth Authentication, r database.UserRepository, logger hclog.Logger) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
authErr := authorizedRefreshToken(ctx, auth, r, logger, request)
userID, authErr := authorizedRefreshToken(ctx, auth, r, logger, request)
if authErr != nil {
return nil, authErr
}
ctx = context.WithValue(ctx, UserIDKey{}, userID)
return next(ctx, request)
}
}
}

// authorizedRefreshToken validates the refresh token.
func authorizedRefreshToken(ctx context.Context, auth Authentication, r database.UserRepository, logger hclog.Logger, request interface{}) error {
func authorizedRefreshToken(ctx context.Context, auth Authentication, r database.UserRepository, logger hclog.Logger, request interface{}) (string, error) {
token, err := extractValue(request, "refresh_token")
if err != nil {
logger.Error("extract value token failed", "err", err)
cusErr := utils.NewErrorResponse(utils.BadRequest)
return cusErr
return "", cusErr
}
logger.Debug("token present in header", token)

userID, customKey, err := auth.ValidateRefreshToken(token)
if err != nil {
logger.Error("token validation failed", "error", err)
cusErr := utils.NewErrorResponse(utils.ValidationTokenFailure)
return cusErr
return "", cusErr
}
logger.Debug("refresh token validated")

user, err := r.GetUserByID(ctx, userID)
if err != nil {
logger.Error("You're not authorized. Please try again latter.", err)
cusErr := utils.NewErrorResponse(utils.ValidationTokenFailure)
return cusErr
return "", cusErr
}

actualCustomKey := auth.GenerateCustomKey(user.ID, user.TokenHash)
if customKey != actualCustomKey {
logger.Debug("wrong token: authentication failed")
cusErr := utils.NewErrorResponse(utils.Unauthorized)
return cusErr
return "", cusErr
}
return nil
return userID, nil
}

func extractValue(request interface{}, key string) (string, error) {
Expand Down
12 changes: 12 additions & 0 deletions pkg/authorization/reqresponse.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,15 @@ type CreateNewPasswordWithCodeRequest struct {
Email string `json:"email" validate:"required,email"`
NewPassword string `json:"new_password" validate:"required"`
}

// GenerateAccessTokenRequest is used to generate access token
type GenerateAccessTokenRequest struct {
RefreshToken string `json:"refresh_token" validate:"required"`
}

// GenerateAccessResponse is the response for generate access token
type GenerateAccessResponse struct {
RefreshToken string `json:"refresh_token,omitempty"`
AccessToken string `json:"access_token,omitempty"`
Username string `json:"username,omitempty"`
}
1 change: 1 addition & 0 deletions pkg/authorization/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ type Service interface {
UpdatePassword(ctx context.Context, request *UpdatePasswordRequest) (string, error)
GetForgetPasswordCode(ctx context.Context, email string) error
ResetPassword(ctx context.Context, request *CreateNewPasswordWithCodeRequest) error
GenerateAccessToken(ctx context.Context, request *GenerateAccessTokenRequest) (interface{}, error)
}
25 changes: 25 additions & 0 deletions pkg/authorization/transport/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,13 @@ func NewHTTPHandler(ep endpoints.Set) http.Handler {
encodeResponse,
options...,
))
m.Handle("/generate-access-token", httptransport.NewServer(
ep.GenerateAccessTokenEndpoint,
decodeHTTPGenerateAccessTokenRequest,
encodeResponse,
options...,
))

mux := http.NewServeMux()
mux.Handle("/api/v1/", http.StripPrefix("/api/v1", m))
return mux
Expand Down Expand Up @@ -298,6 +305,24 @@ func decodeHTTPResetPasswordRequest(_ context.Context, r *http.Request) (interfa
}
}

// decodeHTTPGenerateAccessTokenRequest decode request
func decodeHTTPGenerateAccessTokenRequest(_ context.Context, r *http.Request) (interface{}, error) {
if r.Method == "POST" {
var req authorization.GenerateAccessTokenRequest
err := json.NewDecoder(r.Body).Decode(&req)
if err != nil {
return nil, utils.NewErrorResponse(utils.BadRequest)
}
if req.RefreshToken == "" {
return nil, utils.NewErrorResponse(utils.RefreshTokenRequired)
}
return req, nil
} else {
cusErr := utils.NewErrorResponse(utils.MethodNotAllowed)
return nil, cusErr
}
}

func encodeResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")

Expand Down
34 changes: 34 additions & 0 deletions pkg/authorization/users-service.go
Original file line number Diff line number Diff line change
Expand Up @@ -775,3 +775,37 @@ func (s *userService) ResetPassword(ctx context.Context, request *CreateNewPassw
s.logger.Info("Password changed", "userID", user.ID)
return nil
}

// GenerateAccessToken generate access token
func (s *userService) GenerateAccessToken(ctx context.Context, request *GenerateAccessTokenRequest) (interface{}, error) {
userID, ok := ctx.Value(middleware.UserIDKey{}).(string)
if !ok {
s.logger.Error("Error getting userID from context")
cusErr := utils.NewErrorResponse(utils.InternalServerError)
return cusErr.Error(), cusErr
}
user, err := s.repo.GetUserByID(ctx, userID)
if err != nil {
s.logger.Error("Cannot get user", "error", err)
cusErr := utils.NewErrorResponse(utils.InternalServerError)
return cusErr.Error(), cusErr
}
// Check if user is banned
if user.Banned {
s.logger.Error("User is banned", "error", err)
cusErr := utils.NewErrorResponse(utils.Forbidden)
return cusErr.Error(), cusErr
}
accessToken, err := s.auth.GenerateAccessToken(user)
if err != nil {
s.logger.Error("unable to generate access token", "error", err)
cusErr := utils.NewErrorResponse(utils.InternalServerError)
return cusErr.Error(), cusErr
}

s.logger.Debug("Successfully generated new access token")
return GenerateAccessResponse{
AccessToken: accessToken,
Username: user.Username,
}, nil
}

0 comments on commit 65f43a5

Please sign in to comment.