Skip to content

Commit

Permalink
Generate jwt token for user session
Browse files Browse the repository at this point in the history
  • Loading branch information
Aadesh-Baral committed Aug 24, 2022
1 parent bf6475e commit ea9b108
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 3 deletions.
2 changes: 2 additions & 0 deletions backend/server/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ module github.com/baato/before-after
go 1.18

require (
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/gin-gonic/gin v1.8.1
github.com/google/uuid v1.1.2
github.com/spf13/viper v1.12.0
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5
google.golang.org/appengine v1.6.7
Expand Down
3 changes: 3 additions & 0 deletions backend/server/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
Expand Down Expand Up @@ -132,6 +134,7 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
Expand Down
5 changes: 5 additions & 0 deletions backend/server/serializers/users_serializers.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ type UserResponse struct {
IsEmailVerified bool `json:"is_email_verified"`
}

type LoginUserResponse struct {
User UserResponse `json:"user"`
JwtToken string `json:"jwt_token"`
}

func NewUserResponse(user db.User) UserResponse {
return UserResponse{
ID: user.ID,
Expand Down
10 changes: 7 additions & 3 deletions backend/server/services/auth_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func GetOSMUser(token string) (UserInfo, error) {
}

// Create a new user in db if user not found else updates existing user and returns user
func LoginUser(query *db.Queries, userinfo *UserInfo) (serializer.UserResponse, error) {
func LoginUser(query *db.Queries, userinfo *UserInfo) (serializer.LoginUserResponse, error) {

// Check if user exists in db
_, getErr := query.GetUser(context.Background(), userinfo.ID)
Expand All @@ -72,10 +72,14 @@ func LoginUser(query *db.Queries, userinfo *UserInfo) (serializer.UserResponse,
user, err = createUser(query, userinfo)
}
if err != nil {
return serializer.UserResponse{}, err
return serializer.LoginUserResponse{}, err
} else {
userResponse := serializer.NewUserResponse(user)
return userResponse, nil
loginResponse := serializer.LoginUserResponse{
User: userResponse,
JwtToken: GenerateSessionToken(user.ID),
}
return loginResponse, nil
}

}
Expand Down
101 changes: 101 additions & 0 deletions backend/server/services/token_service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package services

import (
"errors"
"fmt"
"time"

"github.com/dgrijalva/jwt-go"
"github.com/google/uuid"
)

const minSecretKeySize = 12

var (
ErrInvalidToken = errors.New("token is invalid")
ErrExpiredToken = errors.New("token is expired")
)

type JwtMaker struct {
secretKey string
}

type Payload struct {
ID uuid.UUID `json:"id"`
UserID int32 `json:"user_id"`
IssuedAt time.Time `json:"issued_at"`
ExpiresAt time.Time `json:"expires_at"`
}

func NewPayload(userid int32, duration time.Duration) (*Payload, error) {
tokenID, err := uuid.NewRandom()
if err != nil {
return nil, err
}

payload := &Payload{
ID: tokenID,
UserID: userid,
IssuedAt: time.Now(),
ExpiresAt: time.Now().Add(duration),
}

return payload, nil
}

func (payload *Payload) Valid() error {
if time.Now().After(payload.ExpiresAt) {
return ErrExpiredToken
}
return nil
}

func NewJWTMaker(secretKey string) (*JwtMaker, error) {
if len(secretKey) < minSecretKeySize {
return nil, fmt.Errorf("secret key is too short")
}
return &JwtMaker{secretKey}, nil
}

// CreateToken generates a JWT token with the given payload.
func (maker *JwtMaker) CreateToken(userid int32, duration time.Duration) (string, *Payload, error) {
payload, err := NewPayload(userid, duration)
if err != nil {
return "", &Payload{}, err
}
jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, payload)
token, err := jwtToken.SignedString([]byte(maker.secretKey))
return token, payload, err
}

// VerifyToken verifies given token string and returns Payload
func (maker *JwtMaker) VerifyToken(token string) (*Payload, error) {

// func to test signing method used to create string
keyFunc := func(token *jwt.Token) (interface{}, error) {
_, ok := token.Method.(*jwt.SigningMethodHMAC)
if !ok {
return nil, ErrInvalidToken
}
return []byte(maker.secretKey), nil
}

// Parse token and return err if invalid or expired
jwtToken, err := jwt.ParseWithClaims(token, &Payload{}, keyFunc)

// Check if the error returned is for invalid token or expired token
if err != nil {
verr, ok := err.(*jwt.ValidationError)
if ok && errors.Is(verr.Inner, ErrExpiredToken) {
return nil, ErrExpiredToken
}
return nil, ErrInvalidToken
}

payload, ok := jwtToken.Claims.(*Payload)
if !ok {
return nil, ErrInvalidToken
}

return payload, nil
}
1 change: 1 addition & 0 deletions backend/server/util/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type Config struct {
SMTPPassword string `mapstructure:"SMTP_PASSWORD"`
MAILCC string `mapstructure:"MAIL_CC"`
APIBaseURL string `mapstructure:"API_BASE_URL"`
AppSecret string `mapstructure:"APP_SECRET"`
OAUTHClientID string `mapstructure:"OAUTH_CLIENT_ID"`
OAUTHClientSecret string `mapstructure:"OAUTH_CLIENT_SECRET"`
OAUTHScopes []string `mapstructure:"OAUTH_SCOPES"`
Expand Down

0 comments on commit ea9b108

Please sign in to comment.