Skip to content

Commit

Permalink
Merge pull request #36 from Sudarsh1010/improve-verify-otp
Browse files Browse the repository at this point in the history
hash otp before storing cache, create dynamic URL for verify otp page,
  • Loading branch information
Sudarsh1010 authored Nov 13, 2024
2 parents 95d7798 + d73a96a commit 39bbb0c
Show file tree
Hide file tree
Showing 29 changed files with 465 additions and 140 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ require (
github.com/moby/sys/user v0.1.0 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/nrednav/cuid2 v1.0.1
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/nrednav/cuid2 v1.0.1 h1:aYLDCmGxEij7xCdiV6GVSPSlqFOS6sqHKKvBeKjddVY=
github.com/nrednav/cuid2 v1.0.1/go.mod h1:nH9lUYqbtoVsnpy20etw5q1guTjE99Xy4EpmnK5nKm0=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
Expand Down
73 changes: 54 additions & 19 deletions internal/controllers/auth_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package controllers

import (
"errors"
"fmt"

"keizer-auth/internal/models"
"keizer-auth/internal/services"
"keizer-auth/internal/utils"
"keizer-auth/internal/validators"
Expand All @@ -27,20 +29,27 @@ func (ac *AuthController) SignIn(c *fiber.Ctx) error {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request body"})
}

isValid, userDetails, err := ac.authService.VerifyPassword(body.Email, body.Password)
isValid, user, err := ac.authService.VerifyPassword(
body.Email,
body.Password,
)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
return c.
Status(fiber.StatusInternalServerError).
JSON(fiber.Map{"error": "Internal Server Error"})
}
if !isValid {
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Invalid email or password"})
}

sessionId, err := ac.sessionService.CreateSession(userDetails.ID.String())
sessionId, err := ac.sessionService.CreateSession(user)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to create session"})
return c.
Status(fiber.StatusInternalServerError).
JSON(fiber.Map{"error": "Failed to create session"})
}
utils.SetSessionCookie(c, sessionId)

utils.SetSessionCookie(c, sessionId)
return c.JSON(fiber.Map{"message": "signed in successfully"})
}

Expand All @@ -55,7 +64,9 @@ func (ac *AuthController) SignUp(c *fiber.Ctx) error {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"errors": errors})
}

if err := ac.authService.RegisterUser(user); err != nil {
id, err := ac.authService.RegisterUser(user)
if err != nil {
fmt.Print("&+v\n", err)
if errors.Is(err, gorm.ErrCheckConstraintViolated) {
return c.
Status(fiber.StatusBadRequest).
Expand All @@ -69,7 +80,7 @@ func (ac *AuthController) SignUp(c *fiber.Ctx) error {
JSON(fiber.Map{"error": "Failed to sign up user"})
}

return c.JSON(fiber.Map{"message": "User Signed Up!"})
return c.JSON(fiber.Map{"id": id, "message": "User Signed Up!"})
}

func (ac *AuthController) VerifyOTP(c *fiber.Ctx) error {
Expand All @@ -79,31 +90,55 @@ func (ac *AuthController) VerifyOTP(c *fiber.Ctx) error {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request body"})
}

isOtpValid, err := ac.authService.VerifyOTP(verifyOtpBody)
userID, isOtpValid, err := ac.authService.VerifyOTP(verifyOtpBody)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "OTP not found"})
}
if err.Error() == "otp expired" {
return c.Status(fiber.StatusForbidden).JSON(fiber.Map{"error": "OTP expired"})
if errors.Is(err, gorm.ErrRecordNotFound) || err.Error() == "otp expired" {
return c.
Status(fiber.StatusNotFound).
JSON(fiber.Map{"error": "OTP expired"})
}

return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to verify OTP"})
return c.
Status(fiber.StatusInternalServerError).
JSON(fiber.Map{"error": "Failed to verify OTP"})
}

if !isOtpValid {
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "OTP not valid"})
return c.
Status(fiber.StatusUnauthorized).
JSON(fiber.Map{"error": "OTP not valid"})
}

err = ac.authService.SetIsVerified(verifyOtpBody.Id)
user, err := ac.authService.SetIsVerified(userID)
if err != nil {
return err
}

sessionId, err := ac.sessionService.CreateSession(verifyOtpBody.Id)
sessionID, err := ac.sessionService.CreateSession(user)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to create session"})
return c.
Status(fiber.StatusInternalServerError).
JSON(fiber.Map{"error": "Failed to create session"})
}
utils.SetSessionCookie(c, sessionId)

utils.SetSessionCookie(c, sessionID)
return c.JSON(fiber.Map{"message": "OTP Verified!"})
}

func (ac *AuthController) VerifyTokenHandler(c *fiber.Ctx) error {
sessionID := utils.GetSessionCookie(c)
if sessionID == "" {
return c.
Status(fiber.StatusUnauthorized).
JSON(fiber.Map{"error": "Unauthorized"})
}

user := new(models.User)
if err := ac.sessionService.GetSession(sessionID, user); err != nil {
return c.
Status(fiber.StatusUnauthorized).
JSON(fiber.Map{"error": "Unauthorized"})
}

return c.JSON(user)
}
1 change: 0 additions & 1 deletion internal/database/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,6 @@ func autoMigrate(db *gorm.DB) error {
return db.AutoMigrate(
&models.User{},
&models.Domain{},
&models.Session{},
)
}

Expand Down
8 changes: 7 additions & 1 deletion internal/models/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,18 @@ import (
"time"

"github.com/google/uuid"
"gorm.io/gorm"
)

// Base contains common columns for all tables.
type Base struct {
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt *time.Time `sql:"index" json:"deleted_at"`
ID uuid.UUID `gorm:"type:uuid;default:gen_random_uuid()"`
ID uuid.UUID `gorm:"type:uuid"`
}

func (base *Base) BeforeCreate(tx *gorm.DB) (err error) {
base.ID = uuid.New()
return
}
6 changes: 6 additions & 0 deletions internal/models/redis.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package models

type OTPData struct {
OTPHash string `json:"otp_hash"`
ID string `json:"id"`
}
14 changes: 0 additions & 14 deletions internal/models/session.go

This file was deleted.

1 change: 0 additions & 1 deletion internal/models/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ type User struct {
FirstName string `gorm:"not null;type:varchar(100);default:null" json:"first_name"`
LastName string `gorm:"type:varchar(100);default:null" json:"last_name"`
Base
Sessions []Session
IsVerified bool `gorm:"not null;default:false" json:"is_verified"`
IsActive bool `gorm:"not null;default:false" json:"is_active"`
}
9 changes: 7 additions & 2 deletions internal/repositories/redis_repository.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package repositories

import (
"keizer-auth/internal/database"
"time"

"keizer-auth/internal/database"
)

type RedisRepository struct {
Expand All @@ -19,11 +20,15 @@ func (rr *RedisRepository) Get(key string) (string, error) {
}

// set a key's value with expiration
func (rr *RedisRepository) Set(key string, value string, expiration time.Duration) error {
func (rr *RedisRepository) Set(key string, value interface{}, expiration time.Duration) error {
err := rr.rds.RedisClient.Set(rr.rds.Ctx, key, value, expiration).Err()
return err
}

func (rr *RedisRepository) Expire(key string, expiration time.Duration) error {
return rr.rds.RedisClient.Expire(rr.rds.Ctx, key, expiration).Err()
}

func (rr *RedisRepository) TTL(key string) (time.Duration, error) {
result, err := rr.rds.RedisClient.TTL(rr.rds.Ctx, key).Result()
return result, err
Expand Down
36 changes: 27 additions & 9 deletions internal/repositories/user_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package repositories

import (
"fmt"

"keizer-auth/internal/models"

"gorm.io/gorm"
"gorm.io/gorm/clause"
)

type UserRepository struct {
Expand All @@ -16,7 +18,12 @@ func NewUserRepository(db *gorm.DB) *UserRepository {
}

func (r *UserRepository) CreateUser(user *models.User) error {
return r.db.Create(user).Error
error := r.
db.Model(&user).
Clauses(clause.Returning{}).
Create(user).
Error
return error
}

func (r *UserRepository) GetUser(uuid string) (*models.User, error) {
Expand All @@ -32,19 +39,30 @@ func (r *UserRepository) GetUser(uuid string) (*models.User, error) {
return user, nil
}

func (r *UserRepository) GetUserByStruct(query *models.User) (*models.User, error) {
user := new(models.User)
result := r.db.Where(query).First(user)
func (r *UserRepository) GetUserByEmail(user *models.User) error {
result := r.db.Where(user).First(user)
if result.Error != nil {
if result.Error == gorm.ErrRecordNotFound {
return nil, nil
return nil
}
return nil, fmt.Errorf("error in getting user: %w", result.Error)
return fmt.Errorf("error in getting user: %w", result.Error)
}

return user, nil
return nil
}

func (r *UserRepository) GetUserByStruct(user *models.User) error {
result := r.db.Where(user).First(user)
if result.Error != nil {
if result.Error == gorm.ErrRecordNotFound {
return nil
}
return fmt.Errorf("error in getting user: %w", result.Error)
}

return nil
}

func (r *UserRepository) UpdateUser(id string, updates *models.User) error {
return r.db.Model(&models.User{}).Where("id = ?", id).Updates(updates).Error
func (r *UserRepository) UpdateUser(id string, user *models.User) error {
return r.db.Model(user).Clauses(clause.Returning{}).Where("id = ?", id).Updates(user).Error
}
9 changes: 8 additions & 1 deletion internal/server/routes.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
package server

import (
"strings"

"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cors"
)

func (s *FiberServer) RegisterFiberRoutes() {
s.Use(cors.New(cors.Config{
AllowOrigins: "*",
AllowOriginsFunc: func(origin string) bool {
// TODO: handle cors domain
return strings.Contains(origin, "localhost")
},
AllowCredentials: true,
}))

s.Get("/health", s.healthHandler)
Expand All @@ -19,6 +25,7 @@ func (s *FiberServer) RegisterFiberRoutes() {
auth.Post("/sign-up", s.controllers.Auth.SignUp)
auth.Post("/sign-in", s.controllers.Auth.SignIn)
auth.Post("/verify-otp", s.controllers.Auth.VerifyOTP)
auth.Get("/verify-token", s.controllers.Auth.VerifyTokenHandler)

s.Static("/", "./web/dist")
s.Static("*", "./web/dist/index.html")
Expand Down
Loading

0 comments on commit 39bbb0c

Please sign in to comment.