Merge pull request #205 from anik-ghosh-au7/feat/2fa

update: verify otp resolver and test added
This commit is contained in:
Lakhan Samani 2022-07-23 18:37:04 +05:30 committed by GitHub
commit 8db6649e5c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 153 additions and 11 deletions

View File

@ -45,7 +45,7 @@ func (p *provider) UpsertOTP(ctx context.Context, otpParam *models.OTP) (*models
// GetOTPByEmail to get otp for a given email address
func (p *provider) GetOTPByEmail(ctx context.Context, emailAddress string) (*models.OTP, error) {
var otp *models.OTP
var otp models.OTP
query := fmt.Sprintf("FOR d in %s FILTER d.email == @email RETURN d", models.Collections.OTP)
bindVars := map[string]interface{}{
"email": emailAddress,
@ -64,13 +64,13 @@ func (p *provider) GetOTPByEmail(ctx context.Context, emailAddress string) (*mod
}
break
}
_, err := cursor.ReadDocument(ctx, otp)
_, err := cursor.ReadDocument(ctx, &otp)
if err != nil {
return nil, err
}
}
return otp, nil
return &otp, nil
}
// DeleteOTP to delete otp

View File

@ -33,15 +33,15 @@ func (p *provider) UpsertOTP(ctx context.Context, otp *models.OTP) (*models.OTP,
// GetOTPByEmail to get otp for a given email address
func (p *provider) GetOTPByEmail(ctx context.Context, emailAddress string) (*models.OTP, error) {
var otp *models.OTP
var otp models.OTP
otpCollection := p.db.Collection(models.Collections.OTP, options.Collection())
err := otpCollection.FindOne(ctx, bson.M{"email": emailAddress}).Decode(otp)
err := otpCollection.FindOne(ctx, bson.M{"email": emailAddress}).Decode(&otp)
if err != nil {
return nil, err
}
return otp, nil
return &otp, nil
}
// DeleteOTP to delete otp

View File

@ -32,13 +32,13 @@ func (p *provider) UpsertOTP(ctx context.Context, otp *models.OTP) (*models.OTP,
// GetOTPByEmail to get otp for a given email address
func (p *provider) GetOTPByEmail(ctx context.Context, emailAddress string) (*models.OTP, error) {
var otp *models.OTP
var otp models.OTP
result := p.db.Where("email = ?", emailAddress).First(otp)
result := p.db.Where("email = ?", emailAddress).First(&otp)
if result.Error != nil {
return nil, result.Error
}
return otp, nil
return &otp, nil
}
// DeleteOTP to delete otp

View File

@ -2,11 +2,111 @@ package resolvers
import (
"context"
"fmt"
"strings"
"time"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/cookie"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/memorystore"
"github.com/authorizerdev/authorizer/server/token"
"github.com/authorizerdev/authorizer/server/utils"
log "github.com/sirupsen/logrus"
)
// VerifyOtpResolver resolver for verify otp mutation
func VerifyOtpResolver(ctx context.Context, params model.VerifyOTPRequest) (*model.AuthResponse, error) {
return nil, nil
var res *model.AuthResponse
gc, err := utils.GinContextFromContext(ctx)
if err != nil {
log.Debug("Failed to get GinContext: ", err)
return res, err
}
otp, err := db.Provider.GetOTPByEmail(ctx, params.Email)
if err != nil {
log.Debug("Failed to get otp request by email: ", err)
return res, fmt.Errorf(`invalid email: %s`, err.Error())
}
expiresIn := otp.ExpiresAt - time.Now().Unix()
if params.Otp != otp.Otp || expiresIn < 0 {
log.Debug("Failed to verify otp request: ", err)
return res, fmt.Errorf(`invalid otp: %s`, err.Error())
}
user, err := db.Provider.GetUserByEmail(ctx, params.Email)
if err != nil {
log.Debug("Failed to get user by email: ", err)
return res, err
}
isSignUp := user.EmailVerifiedAt == nil
// TODO - Add Login method in DB
loginMethod := constants.AuthRecipeMethodBasicAuth
roles := strings.Split(user.Roles, ",")
scope := []string{"openid", "email", "profile"}
authToken, err := token.CreateAuthToken(gc, user, roles, scope, loginMethod)
if err != nil {
log.Debug("Failed to create auth token: ", err)
return res, err
}
go func() {
err = db.Provider.DeleteOTP(gc, otp)
if err != nil {
log.Debug("Failed to delete otp: ", err)
}
if isSignUp {
utils.RegisterEvent(ctx, constants.UserSignUpWebhookEvent, loginMethod, user)
} else {
utils.RegisterEvent(ctx, constants.UserLoginWebhookEvent, loginMethod, user)
}
db.Provider.AddSession(ctx, models.Session{
UserID: user.ID,
UserAgent: utils.GetUserAgent(gc.Request),
IP: utils.GetIP(gc.Request),
})
}()
authTokenExpiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
if authTokenExpiresIn <= 0 {
authTokenExpiresIn = 1
}
if authTokenExpiresIn <= 0 {
authTokenExpiresIn = 1
}
res = &model.AuthResponse{
Message: `OTP verified successfully.`,
AccessToken: &authToken.AccessToken.Token,
IDToken: &authToken.IDToken.Token,
ExpiresIn: &authTokenExpiresIn,
User: user.AsAPIUser(),
}
sessionKey := loginMethod + ":" + user.ID
cookie.SetSession(gc, authToken.FingerPrintHash)
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash)
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token)
if authToken.RefreshToken != nil {
res.RefreshToken = &authToken.RefreshToken.Token
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token)
}
return res, nil
}

View File

@ -114,7 +114,7 @@ func TestResolvers(t *testing.T) {
metaTests(t, s)
inviteUserTest(t, s)
validateJwtTokenTest(t, s)
verifyOTPTest(t, s)
webhookLogsTest(t, s) // get logs after above resolver tests are done
deleteWebhookTest(t, s) // delete webhooks (admin resolver)
})

View File

@ -0,0 +1,42 @@
package test
import (
"testing"
"time"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/stretchr/testify/assert"
)
func verifyOTPTest(t *testing.T, s TestSetup) {
t.Helper()
t.Run(`should verify otp`, func(t *testing.T) {
_, ctx := createContext(s)
email := "verify_otp." + s.TestInfo.Email
res, err := resolvers.SignupResolver(ctx, model.SignUpInput{
Email: email,
Password: s.TestInfo.Password,
ConfirmPassword: s.TestInfo.Password,
})
assert.NoError(t, err)
assert.NotNil(t, res)
otp, err := db.Provider.UpsertOTP(ctx, &models.OTP{
Otp: "123456",
Email: email,
ExpiresAt: time.Now().Add(1 * time.Minute).Unix(),
})
assert.Equal(t, email, otp.Email)
assert.Nil(t, res.AccessToken, "access token should be nil")
verifyRes, err := resolvers.VerifyOtpResolver(ctx, model.VerifyOTPRequest{
Otp: "123456",
Email: email,
})
assert.Nil(t, err)
assert.NotEqual(t, verifyRes.AccessToken, "", "access token should not be empty")
cleanData(email)
})
}