Files
memberApp/backend/user/login.go
Adrian Zürcher 690b7f4034
All checks were successful
Build Quasar SPA and Go Backend for memberApp / build-spa (push) Successful in 2m12s
Build Quasar SPA and Go Backend for memberApp / build-backend (amd64, , linux) (push) Successful in 5m8s
Build Quasar SPA and Go Backend for memberApp / build-backend (amd64, .exe, windows) (push) Successful in 5m8s
Build Quasar SPA and Go Backend for memberApp / build-backend (arm, 6, , linux) (push) Successful in 4m57s
Build Quasar SPA and Go Backend for memberApp / build-backend (arm64, , linux) (push) Successful in 5m7s
add new user and role table in app (in progress)
2025-10-14 16:41:20 +02:00

182 lines
4.9 KiB
Go

package user
import (
"backend/dbRequest"
"backend/models"
"backend/utils"
"encoding/json"
"io"
"net/http"
"time"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v5"
_ "modernc.org/sqlite"
)
var JWT_SECRET = []byte("4h5Jza1Fn_zuzu&417%8nH*UH100+55-")
var DOMAIN = "localhost"
var ACCESS_TOKEN_TIME = 15 * time.Minute
var REFRESH_TOKEN_TIME = 72 * time.Hour
func (um *UserManager) Login(c *gin.Context) {
if !um.databaseOpened(c) {
return
}
body, err := io.ReadAll(c.Request.Body)
if err != nil {
c.JSON(http.StatusBadRequest, models.NewJsonErrorResponse(err))
return
}
user := models.User{}
err = json.Unmarshal(body, &user)
if err != nil {
c.JSON(http.StatusBadRequest, models.NewJsonErrorResponse(err))
return
}
if !user.IsValid() {
c.JSON(http.StatusBadRequest, models.NewJsonErrorMessageResponse("user empty"))
return
}
var storedPassword, settingsJsonString string
if err := um.database.QueryRow(dbRequest.DBQueryPassword, user.Name).Scan(&user.Id, &user.Role, &storedPassword, &settingsJsonString); dbRequest.CheckDBError(c, user.Name, err) {
return
}
err = json.Unmarshal([]byte(settingsJsonString), &user.Settings)
if err != nil {
c.JSON(http.StatusBadRequest, models.NewJsonErrorMessageResponse(err.Error()))
return
}
if !utils.CheckPassword(user.Password, storedPassword) {
c.JSON(http.StatusBadRequest, models.NewJsonErrorMessageResponse("wrong password"))
return
}
// ---- Create JWT tokens ----
accessTokenExp := time.Now().Add(ACCESS_TOKEN_TIME)
refreshTokenExp := time.Now().Add(REFRESH_TOKEN_TIME)
// Create token
accessToken := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"id": user.Id,
"username": user.Name,
"role": user.Role,
"type": "access",
"exp": accessTokenExp.Unix(),
})
refreshToken := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"id": user.Id,
"username": user.Name,
"role": user.Role,
"type": "refresh",
"exp": refreshTokenExp.Unix(),
})
accessString, err := accessToken.SignedString(JWT_SECRET)
if err != nil {
c.JSON(http.StatusInternalServerError, models.NewJsonErrorMessageResponse("could not create access token"))
return
}
refreshString, err := refreshToken.SignedString(JWT_SECRET)
if err != nil {
c.JSON(http.StatusInternalServerError, models.NewJsonErrorMessageResponse("could not create refresh token"))
return
}
// ---- Set secure cookies ----
secure := gin.Mode() == gin.ReleaseMode
c.SetCookie("access_token", accessString, int(time.Until(accessTokenExp).Seconds()),
"/", "", secure, true) // Path=/, Secure=true (only HTTPS), HttpOnly=true
c.SetCookie("refresh_token", refreshString, int(time.Until(refreshTokenExp).Seconds()),
"/", "", secure, true)
c.JSON(http.StatusOK, gin.H{
"message": "login successful",
"id": user.Id,
"user": user.Name,
"role": user.Role,
"settings": user.Settings,
})
}
func (um *UserManager) Refresh(c *gin.Context) {
refreshCookie, err := c.Cookie("refresh_token")
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"message": "no refresh token"})
return
}
token, err := jwt.Parse(refreshCookie, func(token *jwt.Token) (any, error) {
return JWT_SECRET, nil
})
if err != nil || !token.Valid {
c.JSON(http.StatusUnauthorized, gin.H{"message": "invalid refresh token"})
return
}
claims, ok := token.Claims.(jwt.MapClaims)
if !ok || claims["type"] != "refresh" {
c.JSON(http.StatusUnauthorized, gin.H{"message": "invalid token type"})
return
}
username := claims["username"].(string)
id := claims["id"].(float64)
role := claims["role"].(string)
// new access token
accessExp := time.Now().Add(ACCESS_TOKEN_TIME)
newAccess := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"id": id,
"username": username,
"role": role,
"exp": accessExp.Unix(),
})
accessString, _ := newAccess.SignedString(JWT_SECRET)
c.SetCookie("access_token", accessString, int(time.Until(accessExp).Seconds()), "/", DOMAIN, gin.Mode() == gin.ReleaseMode, true)
c.JSON(http.StatusOK, gin.H{"message": "token refreshed"})
}
func (um *UserManager) Me(c *gin.Context) {
// Read access token from cookie
cookie, err := c.Cookie("access_token")
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"message": "not logged in"})
return
}
// Verify token
token, err := jwt.Parse(cookie, func(t *jwt.Token) (any, error) {
return JWT_SECRET, nil
})
if err != nil || !token.Valid {
c.JSON(http.StatusUnauthorized, gin.H{"message": "invalid token"})
return
}
claims := token.Claims.(jwt.MapClaims)
c.JSON(http.StatusOK, gin.H{
"id": claims["id"],
"user": claims["username"],
"role": claims["role"],
})
}
func (um *UserManager) Logout(c *gin.Context) {
secure := gin.Mode() == gin.ReleaseMode
c.SetCookie("access_token", "", -1, "/", DOMAIN, secure, true)
c.SetCookie("refresh_token", "", -1, "/", DOMAIN, secure, true)
c.JSON(http.StatusOK, gin.H{"message": "logged out"})
}