feat: add auth middleware, and update event post to make use of it

main
Pau Costa Ferrer 2024-07-12 20:37:31 +02:00
parent fce349a31d
commit 73bc1972c7
7 changed files with 96 additions and 5 deletions

View File

@ -17,3 +17,5 @@ content-type: application/json
"email": "test@example.com",
"password": "examplePassword"
}
> {% client.global.set("auth_token", response.body.jwt); %}

View File

@ -5,6 +5,7 @@ GET http://localhost:8080/events
### Post a new event
POST http://localhost:8080/events/
content-type: application/json
Authorization: Bearer {{auth_token}}
{
"name": "My test event",

View File

@ -0,0 +1,6 @@
{
"dev": {
"name": "value",
"auth_token": ""
}
}

49
middleware/middleware.go Normal file
View File

@ -0,0 +1,49 @@
package middleware
import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v5"
"net/http"
"os"
"time"
"udemy_httpserver/models"
)
func RequireAuth(c *gin.Context) {
// Get JWT from cookie
tokenString := c.GetHeader("Authorization")
if tokenString == "" {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
c.Abort()
return
}
tokenString = tokenString[7:] // Remove "Bearer " from the token
// Decode/validate it
token, _ := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
// Don't forget to validate the alg is what you expect:
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
// hmacSampleSecret is a []byte containing your secret, e.g. []byte("my_secret_key")
return []byte(os.Getenv("JWT_SECRET")), nil
})
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
// Check the expiry date
if float64(time.Now().Unix()) > claims["exp"].(float64) {
c.AbortWithStatus(http.StatusUnauthorized)
}
// Validate that the user exists in the database
user, err := models.FindOneByID(int(claims["sub"].(float64)))
if err != nil || user == nil {
c.AbortWithStatus(http.StatusUnauthorized)
}
c.Set("user", user)
c.Next()
} else {
c.AbortWithStatus(http.StatusUnauthorized)
}
}

View File

@ -1,6 +1,7 @@
package models
import (
"database/sql"
"fmt"
"github.com/golang-jwt/jwt/v5"
"golang.org/x/crypto/bcrypt"
@ -35,6 +36,28 @@ func FindOneByEmail(email string) (*User, error) {
selectQuery := `SELECT * FROM users WHERE email = ?`
dbResponse, err := db.Conn.Query(selectQuery, email)
defer closeDbConnection(dbResponse)
if err != nil {
fmt.Println("Something went wrong trying to find a user")
return nil, err
}
if dbResponse.Next() {
var user User
err = dbResponse.Scan(&user.ID, &user.Name, &user.Email, &user.Password)
if err != nil {
fmt.Println("Something went wrong parsing the database row")
return nil, err
}
return &user, nil
}
return nil, nil
}
func FindOneByID(id int) (*User, error) {
selectQuery := `SELECT * FROM users WHERE id =?`
dbResponse, err := db.Conn.Query(selectQuery, id)
defer closeDbConnection(dbResponse)
if err != nil {
fmt.Println("Something went wrong trying to find a user")
return nil, err
@ -68,3 +91,9 @@ func (u *User) GetJWToken() (string, error) {
}
return tokenString, nil
}
func closeDbConnection(dbResponse *sql.Rows) {
err := dbResponse.Close()
if err != nil {
fmt.Println("Something went wrong closing the database row")
}
}

View File

@ -15,7 +15,8 @@ func postEvent(c *gin.Context) {
return
}
// Todo: implement user auth
event.UserID = 0
user, _ := c.Get("user")
event.UserID = user.(*models.User).ID
err = event.Save()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})

View File

@ -1,12 +1,15 @@
package routes
import "github.com/gin-gonic/gin"
import (
"github.com/gin-gonic/gin"
"udemy_httpserver/middleware"
)
func RegisterRoutes(router *gin.Engine) {
router.POST("/events", postEvent)
router.POST("/events", middleware.RequireAuth, postEvent)
router.GET("/events", getEvents)
router.GET("/events/:id", getSingleEvent)
router.PUT("/events/:id", updateEvent)
router.PUT("/events/:id", middleware.RequireAuth, updateEvent)
router.POST("/auth/register", registerUser)
router.POST("/auth/login", Login)