Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 27 additions & 17 deletions internal/handlers/send_otp.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ import (

func (g *GothServer) SendOTP(ctx context.Context, req *goth.SendOTPRequest) (*goth.SendOTPResponse, error) {
// OTPPurpose is a enum type with specified values for each type.
// 0 - MfaType (authenticated)
// 1 - Password reset (user should be authenticated, validate email via access token)
// 2 - Email Verification (can accept email from any user in this case).
purpose := req.Purpose
// 0 - MfaType (authenticated user is required).
// 1 - Password reset (user could be auth/unauthenticated).
// 2 - Email Verification (user could be auth/unauthenticated).


switch purpose {
case 0:
switch req.Purpose {
case goth.OTPPurposeMfa:
// get the email of the authenticated user from the access token.
claims, ok := interceptors.GetClaims(ctx)
if !ok {
Expand Down Expand Up @@ -71,16 +71,11 @@ func (g *GothServer) SendOTP(ctx context.Context, req *goth.SendOTPRequest) (*go
ExpiresInSeconds: 300,
}, nil

case 1:
// get the email of the authenticated user from the access token.
claims, ok := interceptors.GetClaims(ctx)
if !ok {
return nil, status.Error(codes.Internal, "failed getting claims")
}
case goth.OTPPurposePasswordReset:

email := claims.Email
userID := claims.UserID
email := req.Email

// generate + hash the otp and then store to redis.
otp, err := helpers.GenerateOTP()
if err != nil {
return nil, status.Error(codes.Internal, "failed generating OTP")
Expand All @@ -94,7 +89,7 @@ func (g *GothServer) SendOTP(ctx context.Context, req *goth.SendOTPRequest) (*go
// store to redis.
err = g.rdb.Set(
ctx,
"password_reset:email_otp" + userID,
"password_reset:email_otp:" + email,
hashedOTP,
5*time.Minute,
).Err()
Expand All @@ -121,10 +116,25 @@ func (g *GothServer) SendOTP(ctx context.Context, req *goth.SendOTPRequest) (*go
ExpiresInSeconds: 300,
}, nil

case 3:
case goth.OTPPurposeEmailVerify:
// get the email once again.
email := req.Email

// check if the user's account is already verified.
var isVerified bool
err := g.db.QueryRow(
ctx,
`SELECT email_verified FROM users
WHERE email = $1`,
email,
).Scan(&isVerified)
if err != nil {
return nil, status.Error(codes.Internal, "failed sending email to user")
}
if isVerified == true {
return nil, status.Error(codes.AlreadyExists, "your account is already verified")
}

otp, err := helpers.GenerateOTP()
if err != nil {
return nil, status.Error(codes.Internal, "failed generating OTP")
Expand All @@ -138,7 +148,7 @@ func (g *GothServer) SendOTP(ctx context.Context, req *goth.SendOTPRequest) (*go
// store to redis.
err = g.rdb.Set(
ctx,
"acc_verify:email_otp" + email,
"acc_verify:email_otp:" + email,
hashedOTP,
5*time.Minute,
).Err()
Expand Down
2 changes: 1 addition & 1 deletion internal/helpers/hash_otp.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import "golang.org/x/crypto/bcrypt"
func GenerateHash(input string) (string, error) {
hashedBytes, err := bcrypt.GenerateFromPassword([]byte(input), 10)
if err != nil {
return "", nil
return "", err
}

hashedString := string(hashedBytes)
Expand Down
1 change: 0 additions & 1 deletion internal/interceptors/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ func AuthInterceptor(ctx context.Context, req any, info *grpc.UnaryServerInfo, h
switch info.FullMethod {
case "/goth.GothService/Login",
"/goth.GothService/Signup",
"/goth.GothService/SendOTP",
"/goth.GothService/VerifyOTP":
return handler(ctx, req)
}
Expand Down