mirror of
https://git.freecumextremist.com/grumbulon/pomme.git
synced 2024-11-22 17:03:46 +00:00
173 lines
3.8 KiB
Go
173 lines
3.8 KiB
Go
package api
|
|
|
|
import (
|
|
"net/http"
|
|
"time"
|
|
|
|
"dns.froth.zone/pomme/internal"
|
|
"github.com/go-chi/render"
|
|
"golang.org/x/crypto/bcrypt"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// Auth godoc
|
|
//
|
|
// @Summary authenticate as a regular user
|
|
// @Description login to Pomme
|
|
//
|
|
// @Description Rate limited: 5 requests every 5 second
|
|
//
|
|
// @Tags accounts
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param username query string true "Username"
|
|
// @Param password query string true "Password"
|
|
// @Success 200 {object} internal.SwaggerGenericResponse[internal.Response]
|
|
// @failure 401 {object} internal.SwaggerGenericResponse[internal.Response] "authFailed is a 401 error when logging in fails, includes realm"
|
|
// @Router /api/login [post]
|
|
func Login(w http.ResponseWriter, r *http.Request) {
|
|
var result internal.User
|
|
|
|
if _, err := r.Cookie("jwt"); err == nil {
|
|
logger := newResponder(Response[any]{
|
|
Message: "already logged in",
|
|
Status: http.StatusOK,
|
|
})
|
|
logger.apiError(w, r)
|
|
logger.writeLogEntry()
|
|
|
|
return
|
|
}
|
|
|
|
err := r.ParseForm()
|
|
if err != nil {
|
|
logger := newResponder(Response[any]{
|
|
Message: "unable to parse request",
|
|
Status: http.StatusInternalServerError,
|
|
Err: err.Error(),
|
|
})
|
|
logger.apiError(w, r)
|
|
logger.writeLogEntry()
|
|
|
|
return
|
|
}
|
|
|
|
username := r.Form.Get("username")
|
|
|
|
password := r.Form.Get("password")
|
|
|
|
if username == "" {
|
|
logger := newResponder(Response[any]{
|
|
Message: "no username provided",
|
|
Status: http.StatusInternalServerError,
|
|
})
|
|
logger.apiError(w, r)
|
|
|
|
return
|
|
}
|
|
|
|
if password == "" {
|
|
logger := newResponder(Response[any]{
|
|
Message: "no password provided",
|
|
Status: http.StatusInternalServerError,
|
|
})
|
|
logger.apiError(w, r)
|
|
|
|
return
|
|
}
|
|
|
|
db, ok := r.Context().Value(keyPrincipalContextID).(*gorm.DB)
|
|
if !ok {
|
|
logger := newResponder(Response[any]{
|
|
Message: "no password provided",
|
|
Status: http.StatusInternalServerError,
|
|
Err: "DB connection failed",
|
|
})
|
|
logger.apiError(w, r)
|
|
logger.writeLogEntry()
|
|
|
|
return
|
|
}
|
|
|
|
db.Where("username = ?", username).First(&result)
|
|
|
|
if result.Username == "" {
|
|
logger := newResponder(Response[any]{
|
|
Message: "login failed",
|
|
Status: http.StatusUnauthorized,
|
|
Realm: "authentication",
|
|
})
|
|
logger.apiError(w, r)
|
|
logger.writeLogEntry()
|
|
|
|
return
|
|
}
|
|
|
|
err = bcrypt.CompareHashAndPassword([]byte(result.HashedPassword), []byte(password))
|
|
|
|
if err != nil {
|
|
logger := newResponder(Response[any]{
|
|
Message: "login failed",
|
|
Status: http.StatusUnauthorized,
|
|
Realm: "authentication",
|
|
})
|
|
logger.apiError(w, r)
|
|
logger.writeLogEntry()
|
|
|
|
return
|
|
}
|
|
|
|
token, err := makeToken(username)
|
|
if err != nil {
|
|
logger := newResponder(Response[any]{
|
|
Message: "internal server error",
|
|
Status: http.StatusInternalServerError,
|
|
Err: err.Error(),
|
|
})
|
|
logger.apiError(w, r)
|
|
logger.writeLogEntry()
|
|
|
|
return
|
|
}
|
|
|
|
http.SetCookie(w, &http.Cookie{
|
|
HttpOnly: true,
|
|
Expires: time.Now().Add(1 * time.Hour),
|
|
MaxAge: 3600,
|
|
SameSite: http.SameSiteStrictMode,
|
|
// Comment below to disable HTTPS:
|
|
Secure: true,
|
|
Name: "jwt", // Must be named "jwt" or else the token cannot be searched for by jwtauth.Verifier.
|
|
Value: token,
|
|
})
|
|
|
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
resp := internal.Response{
|
|
Message: "Successfully logged in",
|
|
}
|
|
render.JSON(w, r, resp)
|
|
}
|
|
|
|
// Logout destroys a users JWT cookie.
|
|
func Logout(w http.ResponseWriter, r *http.Request) {
|
|
http.SetCookie(w, &http.Cookie{
|
|
HttpOnly: true,
|
|
MaxAge: -1, // Delete the cookie.
|
|
SameSite: http.SameSiteStrictMode,
|
|
Secure: true,
|
|
Name: "jwt",
|
|
Value: "",
|
|
})
|
|
|
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
resp := internal.Response{
|
|
Message: "Successfully logged out",
|
|
}
|
|
render.JSON(w, r, resp)
|
|
}
|