Add query to get token

Resolves #16
This commit is contained in:
Lakhan Samani 2021-07-15 17:32:55 +05:30
parent 1d6191cbcb
commit 699c49ade0
7 changed files with 123 additions and 42 deletions

View File

@ -16,7 +16,7 @@ type Manager interface {
AddUser(user User) (User, error) AddUser(user User) (User, error)
GetUsers() ([]User, error) GetUsers() ([]User, error)
GetUserByEmail(email string) (User, error) GetUserByEmail(email string) (User, error)
UpdateVerificationTime(verifiedAt int64, email string) error UpdateVerificationTime(verifiedAt int64, id uint) error
AddVerification(verification Verification) (Verification, error) AddVerification(verification Verification) (Verification, error)
GetVerificationByToken(token string) (Verification, error) GetVerificationByToken(token string) (Verification, error)
DeleteToken(email string) error DeleteToken(email string) error

View File

@ -24,8 +24,10 @@ type User struct {
func (user *User) BeforeSave(tx *gorm.DB) error { func (user *User) BeforeSave(tx *gorm.DB) error {
// Modify current operation through tx.Statement, e.g: // Modify current operation through tx.Statement, e.g:
if pw, err := bcrypt.GenerateFromPassword([]byte(user.Password), bcrypt.DefaultCost); err == nil { if user.Password != "" {
tx.Statement.SetColumn("Password", pw) if pw, err := bcrypt.GenerateFromPassword([]byte(user.Password), bcrypt.DefaultCost); err == nil {
tx.Statement.SetColumn("Password", string(pw))
}
} }
return nil return nil
@ -63,8 +65,11 @@ func (mgr *manager) GetUserByEmail(email string) (User, error) {
return user, nil return user, nil
} }
func (mgr *manager) UpdateVerificationTime(verifiedAt int64, email string) error { func (mgr *manager) UpdateVerificationTime(verifiedAt int64, id uint) error {
result := mgr.db.Model(&User{}).Where("email = ?", email).Update("email_verified_at", verifiedAt) user := &User{
ID: id,
}
result := mgr.db.Model(&user).Where("id = ?", id).Update("email_verified_at", verifiedAt)
if result.Error != nil { if result.Error != nil {
return result.Error return result.Error

View File

@ -39,7 +39,8 @@ type ResolverRoot interface {
Query() QueryResolver Query() QueryResolver
} }
type DirectiveRoot struct{} type DirectiveRoot struct {
}
type ComplexityRoot struct { type ComplexityRoot struct {
Error struct { Error struct {
@ -48,9 +49,10 @@ type ComplexityRoot struct {
} }
LoginResponse struct { LoginResponse struct {
AccessToken func(childComplexity int) int AccessToken func(childComplexity int) int
Message func(childComplexity int) int AccessTokenExpiresAt func(childComplexity int) int
User func(childComplexity int) int Message func(childComplexity int) int
User func(childComplexity int) int
} }
Mutation struct { Mutation struct {
@ -104,7 +106,6 @@ type MutationResolver interface {
Login(ctx context.Context, params model.LoginInput) (*model.LoginResponse, error) Login(ctx context.Context, params model.LoginInput) (*model.LoginResponse, error)
Logout(ctx context.Context) (*model.Response, error) Logout(ctx context.Context) (*model.Response, error)
} }
type QueryResolver interface { type QueryResolver interface {
Users(ctx context.Context) ([]*model.User, error) Users(ctx context.Context) ([]*model.User, error)
Token(ctx context.Context) (*model.LoginResponse, error) Token(ctx context.Context) (*model.LoginResponse, error)
@ -146,6 +147,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.LoginResponse.AccessToken(childComplexity), true return e.complexity.LoginResponse.AccessToken(childComplexity), true
case "LoginResponse.accessTokenExpiresAt":
if e.complexity.LoginResponse.AccessTokenExpiresAt == nil {
break
}
return e.complexity.LoginResponse.AccessTokenExpiresAt(childComplexity), true
case "LoginResponse.message": case "LoginResponse.message":
if e.complexity.LoginResponse.Message == nil { if e.complexity.LoginResponse.Message == nil {
break break
@ -457,6 +465,7 @@ type Error {
type LoginResponse { type LoginResponse {
message: String! message: String!
accessToken: String accessToken: String
accessTokenExpiresAt: Int64
user: User user: User
} }
@ -741,6 +750,38 @@ func (ec *executionContext) _LoginResponse_accessToken(ctx context.Context, fiel
return ec.marshalOString2ᚖstring(ctx, field.Selections, res) return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
} }
func (ec *executionContext) _LoginResponse_accessTokenExpiresAt(ctx context.Context, field graphql.CollectedField, obj *model.LoginResponse) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "LoginResponse",
Field: field,
Args: nil,
IsMethod: false,
IsResolver: false,
}
ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.AccessTokenExpiresAt, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
return graphql.Null
}
res := resTmp.(*int64)
fc.Result = res
return ec.marshalOInt642ᚖint64(ctx, field.Selections, res)
}
func (ec *executionContext) _LoginResponse_user(ctx context.Context, field graphql.CollectedField, obj *model.LoginResponse) (ret graphql.Marshaler) { func (ec *executionContext) _LoginResponse_user(ctx context.Context, field graphql.CollectedField, obj *model.LoginResponse) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
@ -2819,7 +2860,7 @@ func (ec *executionContext) ___Type_ofType(ctx context.Context, field graphql.Co
func (ec *executionContext) unmarshalInputLoginInput(ctx context.Context, obj interface{}) (model.LoginInput, error) { func (ec *executionContext) unmarshalInputLoginInput(ctx context.Context, obj interface{}) (model.LoginInput, error) {
var it model.LoginInput var it model.LoginInput
asMap := obj.(map[string]interface{}) var asMap = obj.(map[string]interface{})
for k, v := range asMap { for k, v := range asMap {
switch k { switch k {
@ -2847,7 +2888,7 @@ func (ec *executionContext) unmarshalInputLoginInput(ctx context.Context, obj in
func (ec *executionContext) unmarshalInputSignUpInput(ctx context.Context, obj interface{}) (model.SignUpInput, error) { func (ec *executionContext) unmarshalInputSignUpInput(ctx context.Context, obj interface{}) (model.SignUpInput, error) {
var it model.SignUpInput var it model.SignUpInput
asMap := obj.(map[string]interface{}) var asMap = obj.(map[string]interface{})
for k, v := range asMap { for k, v := range asMap {
switch k { switch k {
@ -2907,7 +2948,7 @@ func (ec *executionContext) unmarshalInputSignUpInput(ctx context.Context, obj i
func (ec *executionContext) unmarshalInputVerifySignupTokenInput(ctx context.Context, obj interface{}) (model.VerifySignupTokenInput, error) { func (ec *executionContext) unmarshalInputVerifySignupTokenInput(ctx context.Context, obj interface{}) (model.VerifySignupTokenInput, error) {
var it model.VerifySignupTokenInput var it model.VerifySignupTokenInput
asMap := obj.(map[string]interface{}) var asMap = obj.(map[string]interface{})
for k, v := range asMap { for k, v := range asMap {
switch k { switch k {
@ -2983,6 +3024,8 @@ func (ec *executionContext) _LoginResponse(ctx context.Context, sel ast.Selectio
} }
case "accessToken": case "accessToken":
out.Values[i] = ec._LoginResponse_accessToken(ctx, field, obj) out.Values[i] = ec._LoginResponse_accessToken(ctx, field, obj)
case "accessTokenExpiresAt":
out.Values[i] = ec._LoginResponse_accessTokenExpiresAt(ctx, field, obj)
case "user": case "user":
out.Values[i] = ec._LoginResponse_user(ctx, field, obj) out.Values[i] = ec._LoginResponse_user(ctx, field, obj)
default: default:

View File

@ -13,9 +13,10 @@ type LoginInput struct {
} }
type LoginResponse struct { type LoginResponse struct {
Message string `json:"message"` Message string `json:"message"`
AccessToken *string `json:"accessToken"` AccessToken *string `json:"accessToken"`
User *User `json:"user"` AccessTokenExpiresAt *int64 `json:"accessTokenExpiresAt"`
User *User `json:"user"`
} }
type Response struct { type Response struct {

View File

@ -34,6 +34,7 @@ type Error {
type LoginResponse { type LoginResponse {
message: String! message: String!
accessToken: String accessToken: String
accessTokenExpiresAt: Int64
user: User user: User
} }

View File

@ -38,23 +38,23 @@ func (r *mutationResolver) VerifySignupToken(ctx context.Context, params model.V
return res, errors.New(`Invalid token`) return res, errors.New(`Invalid token`)
} }
// update email_verified_at in users table
db.Mgr.UpdateVerificationTime(time.Now().Unix(), claim.Email)
// delete from verification table
db.Mgr.DeleteToken(claim.Email)
user, err := db.Mgr.GetUserByEmail(claim.Email) user, err := db.Mgr.GetUserByEmail(claim.Email)
if err != nil { if err != nil {
return res, err return res, err
} }
// update email_verified_at in users table
db.Mgr.UpdateVerificationTime(time.Now().Unix(), user.ID)
// delete from verification table
db.Mgr.DeleteToken(claim.Email)
userIdStr := fmt.Sprintf("%d", user.ID) userIdStr := fmt.Sprintf("%d", user.ID)
refreshToken, _ := utils.CreateAuthToken(utils.UserAuthInfo{ refreshToken, _, _ := utils.CreateAuthToken(utils.UserAuthInfo{
ID: userIdStr, ID: userIdStr,
Email: user.Email, Email: user.Email,
}, enum.RefreshToken) }, enum.RefreshToken)
accessToken, _ := utils.CreateAuthToken(utils.UserAuthInfo{ accessToken, expiresAt, _ := utils.CreateAuthToken(utils.UserAuthInfo{
ID: userIdStr, ID: userIdStr,
Email: user.Email, Email: user.Email,
}, enum.AccessToken) }, enum.AccessToken)
@ -62,8 +62,9 @@ func (r *mutationResolver) VerifySignupToken(ctx context.Context, params model.V
session.SetToken(userIdStr, refreshToken) session.SetToken(userIdStr, refreshToken)
res = &model.LoginResponse{ res = &model.LoginResponse{
Message: `Email verified successfully.`, Message: `Email verified successfully.`,
AccessToken: &accessToken, AccessToken: &accessToken,
AccessTokenExpiresAt: &expiresAt,
User: &model.User{ User: &model.User{
ID: userIdStr, ID: userIdStr,
Email: user.Email, Email: user.Email,
@ -165,18 +166,23 @@ func (r *mutationResolver) Login(ctx context.Context, params model.LoginInput) (
return res, errors.New(`Email not verified`) return res, errors.New(`Email not verified`)
} }
// match password // match password
log.Println("params Pass", params.Password)
log.Println("hashed pass", user.Password)
cost, err := bcrypt.Cost([]byte(user.Password))
log.Println(cost, err)
err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(params.Password)) err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(params.Password))
if err != nil { if err != nil {
log.Println("Compare password error:", err) log.Println("Compare password error:", err)
return res, errors.New(`Invalid Password`) return res, errors.New(`Invalid Password`)
} }
userIdStr := fmt.Sprintf("%d", user.ID) userIdStr := fmt.Sprintf("%d", user.ID)
refreshToken, _ := utils.CreateAuthToken(utils.UserAuthInfo{ refreshToken, _, _ := utils.CreateAuthToken(utils.UserAuthInfo{
ID: userIdStr, ID: userIdStr,
Email: user.Email, Email: user.Email,
}, enum.RefreshToken) }, enum.RefreshToken)
accessToken, _ := utils.CreateAuthToken(utils.UserAuthInfo{ accessToken, expiresAt, _ := utils.CreateAuthToken(utils.UserAuthInfo{
ID: userIdStr, ID: userIdStr,
Email: user.Email, Email: user.Email,
}, enum.AccessToken) }, enum.AccessToken)
@ -184,8 +190,9 @@ func (r *mutationResolver) Login(ctx context.Context, params model.LoginInput) (
session.SetToken(userIdStr, refreshToken) session.SetToken(userIdStr, refreshToken)
res = &model.LoginResponse{ res = &model.LoginResponse{
Message: `Logged in successfully`, Message: `Logged in successfully`,
AccessToken: &accessToken, AccessToken: &accessToken,
AccessTokenExpiresAt: &expiresAt,
User: &model.User{ User: &model.User{
ID: userIdStr, ID: userIdStr,
Email: user.Email, Email: user.Email,
@ -259,22 +266,40 @@ func (r *queryResolver) Token(ctx context.Context) (*model.LoginResponse, error)
return res, err return res, err
} }
claim, err := utils.VerifyAuthToken(token) claim, accessTokenErr := utils.VerifyAuthToken(token)
if err != nil { expiresAt := claim.ExpiresAt
// generate new accessToken
return res, err
}
user, err := db.Mgr.GetUserByEmail(claim.Email) user, err := db.Mgr.GetUserByEmail(claim.Email)
if err != nil { if err != nil {
return res, err return res, err
} }
userIdStr := fmt.Sprintf("%d", user.ID)
sessionToken := session.GetToken(userIdStr)
if sessionToken == "" {
return res, errors.New(`Unauthorized`)
}
// TODO check if session token has expired
if accessTokenErr != nil {
// if access token has expired and refresh/session token is valid
// generate new accessToken
fmt.Println(`here... getting new accesstoken`)
token, expiresAt, _ = utils.CreateAuthToken(utils.UserAuthInfo{
ID: userIdStr,
Email: user.Email,
}, enum.AccessToken)
}
utils.SetCookie(gc, token)
res = &model.LoginResponse{ res = &model.LoginResponse{
Message: `Email verified successfully.`, Message: `Email verified successfully.`,
AccessToken: &token, AccessToken: &token,
AccessTokenExpiresAt: &expiresAt,
User: &model.User{ User: &model.User{
ID: fmt.Sprintf("%d", user.ID), ID: userIdStr,
Email: user.Email, Email: user.Email,
Image: &user.Image, Image: &user.Image,
FirstName: &user.FirstName, FirstName: &user.FirstName,

View File

@ -23,23 +23,29 @@ type UserAuthClaim struct {
UserAuthInfo UserAuthInfo
} }
func CreateAuthToken(user UserAuthInfo, tokenType enum.TokenType) (string, error) { func CreateAuthToken(user UserAuthInfo, tokenType enum.TokenType) (string, int64, error) {
t := jwt.New(jwt.GetSigningMethod(constants.JWT_TYPE)) t := jwt.New(jwt.GetSigningMethod(constants.JWT_TYPE))
expiryBound := time.Hour expiryBound := time.Hour
if tokenType == enum.RefreshToken { if tokenType == enum.RefreshToken {
// expires in 90 days // expires in 1 year
expiryBound = time.Hour * 2160 expiryBound = time.Hour * 8760
} }
expiresAt := time.Now().Add(expiryBound).Unix()
t.Claims = &UserAuthClaim{ t.Claims = &UserAuthClaim{
&jwt.StandardClaims{ &jwt.StandardClaims{
ExpiresAt: time.Now().Add(expiryBound).Unix(), ExpiresAt: expiresAt,
}, },
tokenType.String(), tokenType.String(),
user, user,
} }
return t.SignedString([]byte(constants.JWT_SECRET)) token, err := t.SignedString([]byte(constants.JWT_SECRET))
if err != nil {
return "", 0, err
}
return token, expiresAt, nil
} }
func GetAuthToken(gc *gin.Context) (string, error) { func GetAuthToken(gc *gin.Context) (string, error) {