Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

hash otp before storing cache, create dynamic URL for verify otp page, #36

Merged
merged 1 commit into from
Nov 13, 2024
Merged
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
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
Loading