Skip to content

Commit

Permalink
Merge pull request #2 from isd-sgcu/feature/jwt-service
Browse files Browse the repository at this point in the history
[Feature] Jwt Service
  • Loading branch information
bookpanda authored Dec 23, 2023
2 parents 093f8dd + 7f7df92 commit 5e812cd
Show file tree
Hide file tree
Showing 12 changed files with 330 additions and 17 deletions.
11 changes: 10 additions & 1 deletion src/internal/domain/dto/token/token.dto.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
package token

import "github.com/isd-sgcu/johnjud-auth/src/internal/constant"
import (
"github.com/golang-jwt/jwt/v4"
"github.com/isd-sgcu/johnjud-auth/src/internal/constant"
)

type UserCredential struct {
UserId string `json:"user_id"`
Role constant.Role `json:"role"`
}

type AuthPayload struct {
jwt.RegisteredClaims
UserId string `json:"user_id"`
Role constant.Role `json:"role"`
}
2 changes: 1 addition & 1 deletion src/internal/repository/user/user.repository_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func (t *UserRepositoryTest) SetupTest() {
err = db.AutoMigrate(&model.User{})
assert.NoError(t.T(), err)

userRepository := &repositoryImpl{Db: db}
userRepository := NewRepository(db)

initialUser := &model.User{
Email: faker.Email(),
Expand Down
36 changes: 30 additions & 6 deletions src/internal/service/jwt/jwt.service.go
Original file line number Diff line number Diff line change
@@ -1,28 +1,52 @@
package jwt

import (
"fmt"
_jwt "github.com/golang-jwt/jwt/v4"
"github.com/isd-sgcu/johnjud-auth/src/config"
"github.com/isd-sgcu/johnjud-auth/src/internal/constant"
tokenDto "github.com/isd-sgcu/johnjud-auth/src/internal/domain/dto/token"
"github.com/isd-sgcu/johnjud-auth/src/internal/utils"
"github.com/isd-sgcu/johnjud-auth/src/pkg/strategy"
"github.com/pkg/errors"
"time"
)

type serviceImpl struct {
config config.Jwt
strategy strategy.JwtStrategy
jwtUtil utils.IJwtUtil
}

func NewService(config config.Jwt, strategy strategy.JwtStrategy) *serviceImpl {
return &serviceImpl{config: config, strategy: strategy}
func NewService(config config.Jwt, strategy strategy.JwtStrategy, jwtUtil utils.IJwtUtil) *serviceImpl {
return &serviceImpl{config: config, strategy: strategy, jwtUtil: jwtUtil}
}

func (s *serviceImpl) SignAuth(userId string) (string, error) {
return "", nil
func (s *serviceImpl) SignAuth(userId string, role constant.Role) (string, error) {
payloads := tokenDto.AuthPayload{
RegisteredClaims: _jwt.RegisteredClaims{
Issuer: s.config.Issuer,
ExpiresAt: s.jwtUtil.GetNumericDate(time.Now().Add(time.Second * time.Duration(s.config.ExpiresIn))),
IssuedAt: s.jwtUtil.GetNumericDate(time.Now()),
},
UserId: userId,
Role: role,
}

token := s.jwtUtil.GenerateJwtToken(_jwt.SigningMethodHS256, payloads)

tokenStr, err := s.jwtUtil.SignedTokenString(token, s.config.Secret)
if err != nil {
return "", errors.New(fmt.Sprintf("Error while signing the token due to: %s", err.Error()))
}

return tokenStr, nil
}

func (s *serviceImpl) VerifyAuth(token string) (*_jwt.Token, error) {
return nil, nil
return s.jwtUtil.ParseToken(token, s.strategy.AuthDecode)
}

func (s *serviceImpl) GetConfig() *config.Jwt {
return nil
return &s.config
}
133 changes: 130 additions & 3 deletions src/internal/service/jwt/jwt.service_test.go
Original file line number Diff line number Diff line change
@@ -1,22 +1,149 @@
package jwt

import (
"fmt"
"github.com/go-faker/faker/v4"
"github.com/golang-jwt/jwt/v4"
"github.com/isd-sgcu/johnjud-auth/src/config"
"github.com/isd-sgcu/johnjud-auth/src/pkg/strategy"
"github.com/isd-sgcu/johnjud-auth/src/internal/constant"
tokenDto "github.com/isd-sgcu/johnjud-auth/src/internal/domain/dto/token"
"github.com/isd-sgcu/johnjud-auth/src/mocks/strategy"
"github.com/isd-sgcu/johnjud-auth/src/mocks/utils"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/suite"
"testing"
"time"
)

type JwtServiceTest struct {
suite.Suite
config config.Jwt
strategy *strategy.JwtStrategy
config config.Jwt
userId string
role constant.Role
numericDate *jwt.NumericDate
payloads tokenDto.AuthPayload
token *jwt.Token
}

func TestJwtService(t *testing.T) {
suite.Run(t, new(JwtServiceTest))
}

func (t *JwtServiceTest) SetupTest() {
config := config.Jwt{
Secret: "testSecret",
ExpiresIn: 3600,
Issuer: "testIssuer",
}

userId := faker.UUIDDigit()
role := constant.USER
numericDate := jwt.NewNumericDate(time.Now())

payloads := tokenDto.AuthPayload{
RegisteredClaims: jwt.RegisteredClaims{
Issuer: t.config.Issuer,
ExpiresAt: numericDate,
IssuedAt: numericDate,
},
UserId: userId,
Role: role,
}

token := &jwt.Token{
Header: map[string]interface{}{
"typ": "JWT",
"alg": jwt.SigningMethodHS256.Alg(),
},
Method: jwt.SigningMethodHS256,
Claims: payloads,
}

t.config = config
t.userId = userId
t.role = role
t.numericDate = numericDate
t.payloads = payloads
t.token = token
}

func (t *JwtServiceTest) TestSignAuthSuccess() {
expected := "signedTokenStr"

jwtStrategy := strategy.JwtStrategyMock{}
jwtUtil := utils.JwtUtilMock{}

jwtUtil.On("GetNumericDate", mock.AnythingOfType("time.Time")).Return(t.numericDate)
jwtUtil.On("GenerateJwtToken", jwt.SigningMethodHS256, t.payloads).Return(t.token)
jwtUtil.On("SignedTokenString", t.token, t.config.Secret).Return(expected, nil)

jwtSvc := NewService(t.config, &jwtStrategy, &jwtUtil)
actual, err := jwtSvc.SignAuth(t.userId, t.role)

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

func (t *JwtServiceTest) TestSignAuthSignedStringFailed() {
signedTokenError := errors.New("Some Error")
expected := errors.New(fmt.Sprintf("Error while signing the token due to: %s", signedTokenError.Error()))

jwtStrategy := strategy.JwtStrategyMock{}
jwtUtil := utils.JwtUtilMock{}

jwtUtil.On("GetNumericDate", mock.AnythingOfType("time.Time")).Return(t.numericDate)
jwtUtil.On("GenerateJwtToken", jwt.SigningMethodHS256, t.payloads).Return(t.token)
jwtUtil.On("SignedTokenString", t.token, t.config.Secret).Return("", signedTokenError)

jwtSvc := NewService(t.config, &jwtStrategy, &jwtUtil)
actual, err := jwtSvc.SignAuth(t.userId, t.role)

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

func (t *JwtServiceTest) TestVerifyAuthSuccess() {
tokenStr := "validSignedToken"
expected := t.token

jwtStrategy := strategy.JwtStrategyMock{}
jwtUtil := utils.JwtUtilMock{}

jwtUtil.On("ParseToken", tokenStr, mock.AnythingOfType("jwt.Keyfunc")).Return(expected, nil)

jwtSvc := NewService(t.config, &jwtStrategy, &jwtUtil)
actual, err := jwtSvc.VerifyAuth(tokenStr)

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

func (t *JwtServiceTest) TestVerifyAuthFailed() {
tokenStr := "invalidSignedToken"
expected := errors.New("invalid token")

jwtStrategy := strategy.JwtStrategyMock{}
jwtUtil := utils.JwtUtilMock{}

jwtUtil.On("ParseToken", tokenStr, mock.AnythingOfType("jwt.Keyfunc")).Return(nil, expected)

jwtSvc := NewService(t.config, &jwtStrategy, &jwtUtil)
actual, err := jwtSvc.VerifyAuth(tokenStr)

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

func (t *JwtServiceTest) TestGetConfigSuccess() {
expected := &t.config

jwtStrategy := strategy.JwtStrategyMock{}
jwtUtil := utils.JwtUtilMock{}

jwtSvc := NewService(t.config, &jwtStrategy, &jwtUtil)
actual := jwtSvc.GetConfig()

assert.Equal(t.T(), *expected, *actual)
}
3 changes: 2 additions & 1 deletion src/internal/service/token/token.service.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package token

import (
"github.com/isd-sgcu/johnjud-auth/src/internal/constant"
tokenDto "github.com/isd-sgcu/johnjud-auth/src/internal/domain/dto/token"
"github.com/isd-sgcu/johnjud-auth/src/pkg/service/jwt"
authProto "github.com/isd-sgcu/johnjud-go-proto/johnjud/auth/auth/v1"
Expand All @@ -14,7 +15,7 @@ func NewService(jwtService jwt.Service) *serviceImpl {
return &serviceImpl{jwtService: jwtService}
}

func (s *serviceImpl) CreateCredential(userId string, secret string) (*authProto.Credential, error) {
func (s *serviceImpl) CreateCredential(userId string, role constant.Role) (*authProto.Credential, error) {
return nil, nil
}

Expand Down
55 changes: 55 additions & 0 deletions src/internal/strategy/jwt.strategy_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package strategy

import (
"fmt"
"github.com/golang-jwt/jwt/v4"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
"testing"
)

type JwtStrategyTest struct {
suite.Suite
secret string
}

func TestJwtStrategy(t *testing.T) {
suite.Run(t, new(JwtStrategyTest))
}

func (t *JwtStrategyTest) SetupTest() {
secret := "testSecret"

t.secret = secret
}

func (t *JwtStrategyTest) TestAuthDecodeSuccess() {
token := &jwt.Token{
Method: jwt.SigningMethodHS256,
}
expected := []byte(t.secret)

jwtStrategy := NewJwtStrategy(t.secret)
actual, err := jwtStrategy.AuthDecode(token)

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

func (t *JwtStrategyTest) TestAuthDecodeFailed() {
token := &jwt.Token{
Method: jwt.SigningMethodES256,
Header: map[string]interface{}{
"typ": "JWT",
"alg": jwt.SigningMethodES256.Alg(),
},
}
expected := errors.New(fmt.Sprintf("invalid token %v\n", token.Header["alg"]))

jwtStrategy := NewJwtStrategy(t.secret)
actual, err := jwtStrategy.AuthDecode(token)

assert.Nil(t.T(), actual)
assert.Equal(t.T(), expected.Error(), err.Error())
}
35 changes: 35 additions & 0 deletions src/internal/utils/jwt.utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package utils

import (
"github.com/golang-jwt/jwt/v4"
"time"
)

type IJwtUtil interface {
GenerateJwtToken(method jwt.SigningMethod, payloads jwt.Claims) *jwt.Token
GetNumericDate(time time.Time) *jwt.NumericDate
SignedTokenString(token *jwt.Token, secret string) (string, error)
ParseToken(tokenStr string, keyFunc jwt.Keyfunc) (*jwt.Token, error)
}

type jwtUtilImpl struct{}

func NewJwtUtil() *jwtUtilImpl {
return &jwtUtilImpl{}
}

func (u *jwtUtilImpl) GenerateJwtToken(method jwt.SigningMethod, payloads jwt.Claims) *jwt.Token {
return jwt.NewWithClaims(method, payloads)
}

func (u *jwtUtilImpl) GetNumericDate(time time.Time) *jwt.NumericDate {
return jwt.NewNumericDate(time)
}

func (u *jwtUtilImpl) SignedTokenString(token *jwt.Token, secret string) (string, error) {
return token.SignedString([]byte(secret))
}

func (u *jwtUtilImpl) ParseToken(tokenStr string, keyFunc jwt.Keyfunc) (*jwt.Token, error) {
return jwt.Parse(tokenStr, keyFunc)
}
4 changes: 3 additions & 1 deletion src/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/isd-sgcu/johnjud-auth/src/config"
"github.com/isd-sgcu/johnjud-auth/src/database"
"github.com/isd-sgcu/johnjud-auth/src/internal/strategy"
"github.com/isd-sgcu/johnjud-auth/src/internal/utils"
"github.com/isd-sgcu/johnjud-auth/src/pkg/repository/user"
"github.com/isd-sgcu/johnjud-auth/src/pkg/service/auth"
"github.com/isd-sgcu/johnjud-auth/src/pkg/service/jwt"
Expand Down Expand Up @@ -110,7 +111,8 @@ func main() {
userRepo := user.NewRepository(db)

jwtStrategy := strategy.NewJwtStrategy(conf.Jwt.Secret)
jwtService := jwt.NewService(conf.Jwt, jwtStrategy)
jwtUtil := utils.NewJwtUtil()
jwtService := jwt.NewService(conf.Jwt, jwtStrategy, jwtUtil)

tokenService := token.NewService(jwtService)

Expand Down
18 changes: 18 additions & 0 deletions src/mocks/strategy/strategy.mock.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package strategy

import (
"github.com/golang-jwt/jwt/v4"
"github.com/stretchr/testify/mock"
)

type JwtStrategyMock struct {
mock.Mock
}

func (m *JwtStrategyMock) AuthDecode(token *jwt.Token) (interface{}, error) {
args := m.Called(token)
if args.Get(0) != nil {
return args.Get(0), nil
}
return nil, args.Error(1)
}
Loading

0 comments on commit 5e812cd

Please sign in to comment.