Skip to content
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
3 changes: 2 additions & 1 deletion .env.local
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
MONGO_URI="YOUR_MONGODB_CONNECTION_KEY"
JWT_SECRET="YOUR_JWT_SECRET_KEY"

SENDER_EMAIL="SENDER_EMAIL"
GMAIL_APP_PASSWORD="YOUR_GMAIL_APP_PASSWORD"
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ require (
github.com/joho/godotenv v1.5.1
go.mongodb.org/mongo-driver v1.17.6
golang.org/x/crypto v0.40.0
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
)

require (
Expand Down Expand Up @@ -48,4 +49,5 @@ require (
golang.org/x/text v0.27.0 // indirect
golang.org/x/tools v0.34.0 // indirect
google.golang.org/protobuf v1.36.9 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
)
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,11 @@ golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
70 changes: 68 additions & 2 deletions handlers/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import (
"net/http"
"strings"
"time"

"fmt"

"goth/helpers"
"goth/middlewares"
"goth/models"
Expand All @@ -14,6 +15,7 @@ import (
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"golang.org/x/crypto/bcrypt"
)

Expand Down Expand Up @@ -41,6 +43,34 @@ func (h *AuthHandler) Signup (c *gin.Context) {
user.ID = primitive.NewObjectID()
user.CreatedAt = time.Now()
user.Role = "user"
user.IsVerified = false

// generating a token to send in email
tokenString, err := helpers.GenerateToken(user.ID.Hex(), user.Email, user.UserName, user.Role)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Error generating token"})
return
}
VerificationURL := fmt.Sprintf(`http://localhost:8080/api/auth/verify?token=%s`, tokenString)

// email's preq
userEmail := user.Email
emailSubject := "Verifing the account"
emailBody := fmt.Sprintf(`
<h1>Welcome!</h1><p>Thanks for signing up.</p>
</br>
<a href="%s" target="_blank">Verify</a>
`, VerificationURL)

// spawn a background worker to send email
go func(){
err := helpers.SendEmail(userEmail, emailSubject, emailBody)
if err != nil {
fmt.Printf("Error sending the email to %s\n%s", userEmail, err)
} else {
fmt.Printf("Email sent to %s", userEmail)
}
}()

_, err = h.Collection.InsertOne(context.TODO(), user)
if err != nil {
Expand All @@ -59,7 +89,7 @@ func (h *AuthHandler) Signup (c *gin.Context) {
}

c.JSON(http.StatusCreated, gin.H{
"success": "Signup success!",
"success": "Check your mail inbox for verifying your email",
"user": map[string]interface{} {
"userName": user.UserName,
"email": user.Email,
Expand Down Expand Up @@ -140,3 +170,39 @@ func (h *AuthHandler) Logout (c *gin.Context) {
c.SetCookie("token", " ", -1, "/", "localhost", false, true)
c.JSON(http.StatusOK, gin.H{"success": "Logged out successfully!"})
}

func (h *AuthHandler) VerifyEmail (c *gin.Context) {

// get token from query parameters
tokenString := c.Query("token")

// verify the tokenString
claims, err := helpers.VerifyToken(tokenString)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token res"})
return
}

var updatedUser models.User

filter := bson.M{"email": claims.Email}
update := bson.M{
"$set": bson.M{
"isVerified": true,
},
}

opts := options.FindOneAndUpdate().SetReturnDocument(options.After)

err = h.Collection.FindOneAndUpdate(context.TODO(), filter, update, opts).Decode(&updatedUser)
if err != nil {
if err == mongo.ErrNoDocuments {
c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
return
}
c.JSON(http.StatusInternalServerError, gin.H{"error": "Error iterating"})
return
}

c.JSON(http.StatusOK, gin.H{"success": "Account verified successfully!"})
}
28 changes: 28 additions & 0 deletions helpers/email.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package helpers

import (
"fmt"

"gopkg.in/gomail.v2"
)

func SendEmail (to, subject, body string) error {

senderEmail := GetEnvVar("SENDER_EMAIL")
appPass := GetEnvVar("GMAIL_APP_PASSWORD")

// Set up a new message
m := gomail.NewMessage()
m.SetHeader("From", senderEmail)
m.SetHeader("To", to)
m.SetHeader("Subject", subject)
m.SetBody("text/html", body)

// set up the dialer
d := gomail.NewDialer("smtp.gmail.com", 587, senderEmail, appPass)

if err := d.DialAndSend(m); err != nil {
return fmt.Errorf("could not send email %s", err)
}
return nil
}
21 changes: 21 additions & 0 deletions helpers/env.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package helpers

import (
"os"
"fmt"
"github.com/joho/godotenv"
)

func GetEnvVar(env string) string {
if err := godotenv.Load(); err != nil {
fmt.Printf("Error loading the env variables %s", err)
os.Exit(1)
}

key := os.Getenv(env)
if key == "" {
fmt.Printf("No env variable with key = %s found", key)
os.Exit(1)
}
return key
}
13 changes: 7 additions & 6 deletions models/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ import (
)

type User struct {
ID primitive.ObjectID `json:"id" bson:"_id, omitempty"`
UserName string `json:"userName" bson:"userName" binding:"required"`
Email string `json:"email" bson:"email" binding:"required"`
Role string `json:"role" bson:"role"`
Password string `json:"password" bson:"password" binding:"required"`
CreatedAt time.Time `json:"created_at" bson:"created_at"`
ID primitive.ObjectID `json:"id" bson:"_id, omitempty"`
UserName string `json:"userName" bson:"userName" binding:"required"`
Email string `json:"email" bson:"email" binding:"required"`
Role string `json:"role" bson:"role"`
Password string `json:"password" bson:"password" binding:"required"`
IsVerified bool `json:"isVerified" bson:"isVerified"`
CreatedAt time.Time `json:"created_at" bson:"created_at"`
}
1 change: 1 addition & 0 deletions routes/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ func AuthRoute (router *gin.Engine, authHandler *handlers.AuthHandler) {
api := router.Group("/api/auth")
{
api.POST("/signup", authHandler.Signup)
api.GET("/verify", authHandler.VerifyEmail)
api.POST("/login", authHandler.Login)
api.POST("/logout", middlewares.AuthMiddleware(), authHandler.Logout)
api.GET("/users", middlewares.AuthMiddleware(), authHandler.GetUsers)
Expand Down