David Ervardy
1 year ago
9 changed files with 228 additions and 7 deletions
@ -0,0 +1,115 @@ |
|||||||
|
package controllers |
||||||
|
|
||||||
|
import ( |
||||||
|
"go-crud/initializers" |
||||||
|
"go-crud/models" |
||||||
|
"net/http" |
||||||
|
"os" |
||||||
|
"time" |
||||||
|
|
||||||
|
"github.com/gin-gonic/gin" |
||||||
|
"github.com/golang-jwt/jwt/v4" |
||||||
|
"golang.org/x/crypto/bcrypt" |
||||||
|
) |
||||||
|
|
||||||
|
func Signup(c *gin.Context) { |
||||||
|
// Get the email/pass off req body
|
||||||
|
var body struct { |
||||||
|
Email string |
||||||
|
Password string |
||||||
|
} |
||||||
|
if err := c.ShouldBind(&body); err != nil { |
||||||
|
c.JSON(http.StatusBadRequest, gin.H{ |
||||||
|
"error": "Failed to read body", |
||||||
|
}) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// Hash the password
|
||||||
|
hash, err := bcrypt.GenerateFromPassword([]byte(body.Password), 10) |
||||||
|
if err != nil { |
||||||
|
c.JSON(http.StatusBadRequest, gin.H{ |
||||||
|
"error": "Failed to generate pass hash", |
||||||
|
}) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// Create the user
|
||||||
|
user := models.User{Email: body.Email, Password: string(hash)} |
||||||
|
result := initializers.DB.Create(&user) |
||||||
|
|
||||||
|
if result.Error != nil { |
||||||
|
c.JSON(http.StatusBadRequest, gin.H{ |
||||||
|
"error": "Failed to create user", |
||||||
|
}) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// Respond
|
||||||
|
c.JSON(http.StatusOK, gin.H{}) |
||||||
|
} |
||||||
|
|
||||||
|
func Login(c *gin.Context) { |
||||||
|
// Get the email/pass off req body
|
||||||
|
var body struct { |
||||||
|
Email string |
||||||
|
Password string |
||||||
|
} |
||||||
|
if err := c.ShouldBind(&body); err != nil { |
||||||
|
c.JSON(http.StatusBadRequest, gin.H{ |
||||||
|
"error": "Failed to read body", |
||||||
|
}) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// Look up requested user
|
||||||
|
var user models.User |
||||||
|
initializers.DB.First(&user, "email = ?", body.Email) |
||||||
|
if user.ID == 0 { |
||||||
|
c.JSON(http.StatusBadRequest, gin.H{ |
||||||
|
"error": "Invalid email or pass", |
||||||
|
}) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// Compare sent in pass with saved user pass hash
|
||||||
|
err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(body.Password)) |
||||||
|
|
||||||
|
if err != nil { |
||||||
|
c.JSON(http.StatusBadRequest, gin.H{ |
||||||
|
"error": "Invalid email or pass", |
||||||
|
}) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// Generate a jwt token
|
||||||
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ |
||||||
|
"sub": user.ID, |
||||||
|
"exp": time.Now().Add(time.Hour * 24 * 30).Unix(), |
||||||
|
}) |
||||||
|
|
||||||
|
// Sign and get the complete encoded token as a string using the secret token
|
||||||
|
tokenString, err := token.SignedString([]byte(os.Getenv("SECRET"))) |
||||||
|
|
||||||
|
if err != nil { |
||||||
|
c.JSON(http.StatusBadRequest, gin.H{ |
||||||
|
"error": "Failed to create token", |
||||||
|
}) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// Send it back
|
||||||
|
c.SetSameSite(http.SameSiteLaxMode) |
||||||
|
c.SetCookie("Authorization", tokenString, 3600*24*30, "", "", false, true) |
||||||
|
c.JSON(http.StatusOK, gin.H{ |
||||||
|
"token": tokenString, |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
func Validate(c *gin.Context) { |
||||||
|
user, _ := c.Get("user") |
||||||
|
|
||||||
|
c.JSON(http.StatusOK, gin.H{ |
||||||
|
"message": user, |
||||||
|
}) |
||||||
|
} |
Binary file not shown.
Binary file not shown.
@ -0,0 +1,7 @@ |
|||||||
|
package initializers |
||||||
|
|
||||||
|
import "go-crud/models" |
||||||
|
|
||||||
|
func SyncDatabase() { |
||||||
|
DB.AutoMigrate(&models.User{}) |
||||||
|
} |
@ -0,0 +1,64 @@ |
|||||||
|
package middleware |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
"go-crud/initializers" |
||||||
|
"go-crud/models" |
||||||
|
"net/http" |
||||||
|
"os" |
||||||
|
"time" |
||||||
|
|
||||||
|
"github.com/dgrijalva/jwt-go" |
||||||
|
"github.com/gin-gonic/gin" |
||||||
|
) |
||||||
|
|
||||||
|
func RequireAuth(c *gin.Context) { |
||||||
|
// Get the cookie off req
|
||||||
|
tokenString, err := c.Cookie("Authorization") |
||||||
|
if err != nil { |
||||||
|
c.AbortWithStatus(http.StatusUnauthorized) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// Decode/validate it
|
||||||
|
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { |
||||||
|
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("SECRET")), nil |
||||||
|
}) |
||||||
|
|
||||||
|
if err != nil || !token.Valid { |
||||||
|
c.AbortWithStatus(http.StatusUnauthorized) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
claims, ok := token.Claims.(jwt.MapClaims) |
||||||
|
if !ok { |
||||||
|
c.AbortWithStatus(http.StatusUnauthorized) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// Check the exp
|
||||||
|
if exp, ok := claims["exp"].(float64); ok && float64(time.Now().Unix()) > exp { |
||||||
|
c.AbortWithStatus(http.StatusUnauthorized) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// Find the news with token sub
|
||||||
|
var news models.News |
||||||
|
initializers.DB.First(&news, claims["sub"]) |
||||||
|
|
||||||
|
if news.ID == 0 { |
||||||
|
c.AbortWithStatus(http.StatusUnauthorized) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// Attach to req
|
||||||
|
c.Set("news", news) |
||||||
|
|
||||||
|
// Continue
|
||||||
|
c.Next() |
||||||
|
} |
Loading…
Reference in new issue