Skip to content

Commit

Permalink
Merge pull request #13 from isd-sgcu/JOH-26/refresh-token
Browse files Browse the repository at this point in the history
[JOH-26] Refresh Token
  • Loading branch information
Nitiwat-owen authored Jan 3, 2024
2 parents 32f4d5f + 12e82d8 commit 026adc0
Show file tree
Hide file tree
Showing 6 changed files with 327 additions and 73 deletions.
55 changes: 37 additions & 18 deletions internal/service/auth/auth.service.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"github.com/isd-sgcu/johnjud-auth/pkg/repository/user"
"github.com/isd-sgcu/johnjud-auth/pkg/service/token"

"github.com/google/uuid"
authProto "github.com/isd-sgcu/johnjud-go-proto/johnjud/auth/auth/v1"
"github.com/pkg/errors"
"google.golang.org/grpc/codes"
Expand Down Expand Up @@ -47,10 +46,29 @@ func (s *serviceImpl) Validate(_ context.Context, request *authProto.ValidateReq
}

func (s *serviceImpl) RefreshToken(_ context.Context, request *authProto.RefreshTokenRequest) (*authProto.RefreshTokenResponse, error) {
// find user with refreshToken
// create new Credential
// update refreshToken in db
return nil, nil
refreshTokenCache, err := s.tokenService.FindRefreshTokenCache(request.RefreshToken)
if err != nil {
st, _ := status.FromError(err)
switch st.Code() {
case codes.InvalidArgument:
return nil, status.Error(codes.InvalidArgument, constant.InvalidTokenErrorMessage)
default:
return nil, status.Error(codes.Internal, constant.InternalServerErrorMessage)
}
}
credential, err := s.tokenService.CreateCredential(refreshTokenCache.UserID, refreshTokenCache.Role, refreshTokenCache.AuthSessionID)
if err != nil {
return nil, status.Error(codes.Internal, constant.InternalServerErrorMessage)
}

err = s.tokenService.RemoveRefreshTokenCache(request.RefreshToken)
if err != nil {
return nil, status.Error(codes.Internal, constant.InternalServerErrorMessage)
}

return &authProto.RefreshTokenResponse{
Credential: credential,
}, nil
}

func (s *serviceImpl) SignUp(_ context.Context, request *authProto.SignUpRequest) (*authProto.SignUpResponse, error) {
Expand Down Expand Up @@ -94,7 +112,15 @@ func (s *serviceImpl) SignIn(_ context.Context, request *authProto.SignInRequest
return nil, status.Error(codes.PermissionDenied, constant.IncorrectEmailPasswordErrorMessage)
}

credential, err := s.createAuthSession(user.ID, user.Role)
createAuthSession := &model.AuthSession{
UserID: user.ID,
}
err = s.authRepo.Create(createAuthSession)
if err != nil {
return nil, status.Error(codes.Internal, constant.InternalServerErrorMessage)
}

credential, err := s.tokenService.CreateCredential(user.ID.String(), user.Role, createAuthSession.ID.String())
if err != nil {
return nil, status.Error(codes.Internal, constant.InternalServerErrorMessage)
}
Expand All @@ -108,27 +134,20 @@ func (s *serviceImpl) SignOut(_ context.Context, request *authProto.SignOutReque
return nil, status.Error(codes.Internal, constant.InternalServerErrorMessage)
}

err = s.tokenService.RemoveTokenCache(userCredential.RefreshToken)
err = s.tokenService.RemoveRefreshTokenCache(userCredential.RefreshToken)
if err != nil {
return nil, status.Error(codes.Internal, constant.InternalServerErrorMessage)
}

err = s.authRepo.Delete(userCredential.AuthSessionID)
err = s.tokenService.RemoveAccessTokenCache(userCredential.AuthSessionID)
if err != nil {
return nil, status.Error(codes.Internal, constant.InternalServerErrorMessage)
}

return &authProto.SignOutResponse{IsSuccess: true}, nil
}

func (s *serviceImpl) createAuthSession(userId uuid.UUID, role constant.Role) (*authProto.Credential, error) {
createAuthSession := &model.AuthSession{
UserID: userId,
}
err := s.authRepo.Create(createAuthSession)
err = s.authRepo.Delete(userCredential.AuthSessionID)
if err != nil {
return nil, errors.New("Internal server error")
return nil, status.Error(codes.Internal, constant.InternalServerErrorMessage)
}

return s.tokenService.CreateCredential(userId.String(), role, createAuthSession.ID.String())
return &authProto.SignOutResponse{IsSuccess: true}, nil
}
205 changes: 192 additions & 13 deletions internal/service/auth/auth.service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,12 @@ import (

type AuthServiceTest struct {
suite.Suite
ctx context.Context
signupRequest *authProto.SignUpRequest
signInRequest *authProto.SignInRequest
signOutRequest *authProto.SignOutRequest
validateRequest *authProto.ValidateRequest
ctx context.Context
signupRequest *authProto.SignUpRequest
signInRequest *authProto.SignInRequest
signOutRequest *authProto.SignOutRequest
refreshTokenRequest *authProto.RefreshTokenRequest
validateRequest *authProto.ValidateRequest
}

func TestAuthService(t *testing.T) {
Expand All @@ -55,12 +56,16 @@ func (t *AuthServiceTest) SetupTest() {
validateRequest := &authProto.ValidateRequest{
Token: faker.Word(),
}
refreshTokenRequest := &authProto.RefreshTokenRequest{
RefreshToken: faker.UUIDDigit(),
}

t.ctx = ctx
t.signupRequest = signupRequest
t.signInRequest = signInRequest
t.signOutRequest = signOutRequest
t.validateRequest = validateRequest
t.refreshTokenRequest = refreshTokenRequest
}

func (t *AuthServiceTest) TestSignupSuccess() {
Expand Down Expand Up @@ -434,13 +439,153 @@ func (t *AuthServiceTest) TestValidateFailed() {
assert.Equal(t.T(), expected.Error(), err.Error())
}

func (t *AuthServiceTest) TestRefreshTokenSuccess() {}
func (t *AuthServiceTest) TestRefreshTokenSuccess() {
refreshTokenCache := &tokenDto.RefreshTokenCache{
AuthSessionID: faker.UUIDDigit(),
UserID: faker.UUIDDigit(),
Role: constant.USER,
}
credential := &authProto.Credential{
AccessToken: faker.Word(),
RefreshToken: faker.UUIDDigit(),
ExpiresIn: 3600,
}

expected := &authProto.RefreshTokenResponse{
Credential: credential,
}

controller := gomock.NewController(t.T())

authRepo := mock_auth.NewMockRepository(controller)
userRepo := user.UserRepositoryMock{}
tokenService := token.TokenServiceMock{}
bcryptUtil := utils.BcryptUtilMock{}

tokenService.On("FindRefreshTokenCache", t.refreshTokenRequest.RefreshToken).Return(refreshTokenCache, nil)
tokenService.On("CreateCredential", refreshTokenCache.UserID, refreshTokenCache.Role, refreshTokenCache.AuthSessionID).Return(credential, nil)
tokenService.On("RemoveRefreshTokenCache", t.refreshTokenRequest.RefreshToken).Return(nil)

authSvc := NewService(authRepo, &userRepo, &tokenService, &bcryptUtil)
actual, err := authSvc.RefreshToken(t.ctx, t.refreshTokenRequest)

assert.Nil(t.T(), err)
assert.Equal(t.T(), expected, actual)
}

func (t *AuthServiceTest) TestRefreshTokenInvalid() {
findTokenErr := status.Error(codes.InvalidArgument, "token not found")

expected := status.Error(codes.InvalidArgument, constant.InvalidTokenErrorMessage)

func (t *AuthServiceTest) TestRefreshTokenNotFound() {}
controller := gomock.NewController(t.T())

func (t *AuthServiceTest) TestRefreshTokenCreateCredentialFailed() {}
authRepo := mock_auth.NewMockRepository(controller)
userRepo := user.UserRepositoryMock{}
tokenService := token.TokenServiceMock{}
bcryptUtil := utils.BcryptUtilMock{}

func (t *AuthServiceTest) TestRefreshTokenUpdateTokenFailed() {}
tokenService.On("FindRefreshTokenCache", t.refreshTokenRequest.RefreshToken).Return(nil, findTokenErr)

authSvc := NewService(authRepo, &userRepo, &tokenService, &bcryptUtil)
actual, err := authSvc.RefreshToken(t.ctx, t.refreshTokenRequest)

st, ok := status.FromError(err)
assert.Nil(t.T(), actual)
assert.Equal(t.T(), codes.InvalidArgument, st.Code())
assert.True(t.T(), ok)
assert.Equal(t.T(), expected.Error(), err.Error())
}

func (t *AuthServiceTest) TestRefreshTokenFindTokenFailed() {
findTokenErr := status.Error(codes.Internal, "internal error")

expected := status.Error(codes.Internal, constant.InternalServerErrorMessage)

controller := gomock.NewController(t.T())

authRepo := mock_auth.NewMockRepository(controller)
userRepo := user.UserRepositoryMock{}
tokenService := token.TokenServiceMock{}
bcryptUtil := utils.BcryptUtilMock{}

tokenService.On("FindRefreshTokenCache", t.refreshTokenRequest.RefreshToken).Return(nil, findTokenErr)

authSvc := NewService(authRepo, &userRepo, &tokenService, &bcryptUtil)
actual, err := authSvc.RefreshToken(t.ctx, t.refreshTokenRequest)

st, ok := status.FromError(err)
assert.Nil(t.T(), actual)
assert.Equal(t.T(), codes.Internal, st.Code())
assert.True(t.T(), ok)
assert.Equal(t.T(), expected.Error(), err.Error())
}

func (t *AuthServiceTest) TestRefreshTokenCreateCredentialFailed() {
refreshTokenCache := &tokenDto.RefreshTokenCache{
AuthSessionID: faker.UUIDDigit(),
UserID: faker.UUIDDigit(),
Role: constant.USER,
}
createCredentialErr := errors.New("internal error")

expected := status.Error(codes.Internal, constant.InternalServerErrorMessage)

controller := gomock.NewController(t.T())

authRepo := mock_auth.NewMockRepository(controller)
userRepo := user.UserRepositoryMock{}
tokenService := token.TokenServiceMock{}
bcryptUtil := utils.BcryptUtilMock{}

tokenService.On("FindRefreshTokenCache", t.refreshTokenRequest.RefreshToken).Return(refreshTokenCache, nil)
tokenService.On("CreateCredential", refreshTokenCache.UserID, refreshTokenCache.Role, refreshTokenCache.AuthSessionID).Return(nil, createCredentialErr)

authSvc := NewService(authRepo, &userRepo, &tokenService, &bcryptUtil)
actual, err := authSvc.RefreshToken(t.ctx, t.refreshTokenRequest)

st, ok := status.FromError(err)
assert.Nil(t.T(), actual)
assert.Equal(t.T(), codes.Internal, st.Code())
assert.True(t.T(), ok)
assert.Equal(t.T(), expected.Error(), err.Error())
}

func (t *AuthServiceTest) TestRefreshTokenRemoveTokenFailed() {
refreshTokenCache := &tokenDto.RefreshTokenCache{
AuthSessionID: faker.UUIDDigit(),
UserID: faker.UUIDDigit(),
Role: constant.USER,
}
credential := &authProto.Credential{
AccessToken: faker.Word(),
RefreshToken: faker.UUIDDigit(),
ExpiresIn: 3600,
}
removeTokenErr := errors.New("internal error")

expected := status.Error(codes.Internal, constant.InternalServerErrorMessage)

controller := gomock.NewController(t.T())

authRepo := mock_auth.NewMockRepository(controller)
userRepo := user.UserRepositoryMock{}
tokenService := token.TokenServiceMock{}
bcryptUtil := utils.BcryptUtilMock{}

tokenService.On("FindRefreshTokenCache", t.refreshTokenRequest.RefreshToken).Return(refreshTokenCache, nil)
tokenService.On("CreateCredential", refreshTokenCache.UserID, refreshTokenCache.Role, refreshTokenCache.AuthSessionID).Return(credential, nil)
tokenService.On("RemoveRefreshTokenCache", t.refreshTokenRequest.RefreshToken).Return(removeTokenErr)

authSvc := NewService(authRepo, &userRepo, &tokenService, &bcryptUtil)
actual, err := authSvc.RefreshToken(t.ctx, t.refreshTokenRequest)

st, ok := status.FromError(err)
assert.Nil(t.T(), actual)
assert.Equal(t.T(), codes.Internal, st.Code())
assert.True(t.T(), ok)
assert.Equal(t.T(), expected.Error(), err.Error())
}

func (t *AuthServiceTest) TestSignOutSuccess() {
userCredential := &tokenDto.UserCredential{
Expand All @@ -462,7 +607,8 @@ func (t *AuthServiceTest) TestSignOutSuccess() {
bcryptUtil := utils.BcryptUtilMock{}

tokenService.On("Validate", t.signOutRequest.Token).Return(userCredential, nil)
tokenService.On("RemoveTokenCache", userCredential.RefreshToken).Return(nil)
tokenService.On("RemoveRefreshTokenCache", userCredential.RefreshToken).Return(nil)
tokenService.On("RemoveAccessTokenCache", userCredential.AuthSessionID).Return(nil)
authRepo.EXPECT().Delete(userCredential.AuthSessionID).Return(nil)

authSvc := NewService(authRepo, &userRepo, &tokenService, &bcryptUtil)
Expand Down Expand Up @@ -494,7 +640,38 @@ func (t *AuthServiceTest) TestSignOutValidateFailed() {
assert.Equal(t.T(), expected.Error(), err.Error())
}

func (t *AuthServiceTest) TestSignOutRemoveTokenCacheFailed() {
func (t *AuthServiceTest) TestSignOutRemoveRefreshTokenCacheFailed() {
userCredential := &tokenDto.UserCredential{
UserID: faker.UUIDDigit(),
Role: constant.USER,
AuthSessionID: faker.UUIDDigit(),
RefreshToken: faker.UUIDDigit(),
}
removeTokenErr := errors.New("internal server error")

expected := status.Error(codes.Internal, constant.InternalServerErrorMessage)

controller := gomock.NewController(t.T())

authRepo := mock_auth.NewMockRepository(controller)
userRepo := user.UserRepositoryMock{}
tokenService := token.TokenServiceMock{}
bcryptUtil := utils.BcryptUtilMock{}

tokenService.On("Validate", t.signOutRequest.Token).Return(userCredential, nil)
tokenService.On("RemoveRefreshTokenCache", userCredential.RefreshToken).Return(removeTokenErr)

authSvc := NewService(authRepo, &userRepo, &tokenService, &bcryptUtil)
actual, err := authSvc.SignOut(t.ctx, t.signOutRequest)

st, ok := status.FromError(err)
assert.Nil(t.T(), actual)
assert.Equal(t.T(), codes.Internal, st.Code())
assert.True(t.T(), ok)
assert.Equal(t.T(), expected.Error(), err.Error())
}

func (t *AuthServiceTest) TestSignOutRemoveAccessTokenCacheFailed() {
userCredential := &tokenDto.UserCredential{
UserID: faker.UUIDDigit(),
Role: constant.USER,
Expand All @@ -513,7 +690,8 @@ func (t *AuthServiceTest) TestSignOutRemoveTokenCacheFailed() {
bcryptUtil := utils.BcryptUtilMock{}

tokenService.On("Validate", t.signOutRequest.Token).Return(userCredential, nil)
tokenService.On("RemoveTokenCache", userCredential.RefreshToken).Return(removeTokenErr)
tokenService.On("RemoveRefreshTokenCache", userCredential.RefreshToken).Return(nil)
tokenService.On("RemoveAccessTokenCache", userCredential.AuthSessionID).Return(removeTokenErr)

authSvc := NewService(authRepo, &userRepo, &tokenService, &bcryptUtil)
actual, err := authSvc.SignOut(t.ctx, t.signOutRequest)
Expand Down Expand Up @@ -544,7 +722,8 @@ func (t *AuthServiceTest) TestSignOutDeleteAuthSessionFailed() {
bcryptUtil := utils.BcryptUtilMock{}

tokenService.On("Validate", t.signOutRequest.Token).Return(userCredential, nil)
tokenService.On("RemoveTokenCache", userCredential.RefreshToken).Return(nil)
tokenService.On("RemoveRefreshTokenCache", userCredential.RefreshToken).Return(nil)
tokenService.On("RemoveAccessTokenCache", userCredential.AuthSessionID).Return(nil)
authRepo.EXPECT().Delete(userCredential.AuthSessionID).Return(deleteAuthErr)

authSvc := NewService(authRepo, &userRepo, &tokenService, &bcryptUtil)
Expand Down
Loading

0 comments on commit 026adc0

Please sign in to comment.