refac: reamaining auth surface rewrite, T0

This commit is contained in:
fsylvain committed 2026-09-24 05:11:49 +02:00
1 parent 6d510d2805
commit aa865af2e9
50 files changed
+3723 -149

No files matched your search

+27 -16
View File
@@ -2,6 +2,7 @@ package article
import (
"RenewCMS/api/pkg/apperr"
"RenewCMS/api/pkg/claims"
validator "RenewCMS/api/pkg/validator"
domain "RenewCMS/internal/domain/article"
"RenewCMS/internal/infrastructure/useCases"
@@ -13,15 +14,15 @@ import (
"strconv"
"github.com/go-chi/chi/v5"
"github.com/go-chi/jwtauth/v5"
)
type Handler struct {
CreateUseCase *useCases.CreateArticleUseCase
GetUseCase *useCases.GetArticleUseCase
ListUseCase *useCases.ListArticlesUseCase
DeleteUseCase *useCases.DeleteArticleUseCase
UpdateUseCase *useCases.UpdateArticleUseCase
CreateUseCase *useCases.CreateArticleUseCase
GetUseCase *useCases.GetArticleUseCase
ListUseCase *useCases.ListArticlesUseCase
DeleteUseCase *useCases.DeleteArticleUseCase
UpdateUseCase *useCases.UpdateArticleUseCase
GetUserUseCase *useCases.GetUserUseCase
}
type PostArticle struct {
@@ -29,13 +30,14 @@ type PostArticle struct {
Body string `json:"body" validate:"required,max=10000"`
}
func NewHandler(create *useCases.CreateArticleUseCase, get *useCases.GetArticleUseCase, list *useCases.ListArticlesUseCase, delete *useCases.DeleteArticleUseCase, update *useCases.UpdateArticleUseCase) *Handler {
func NewHandler(create *useCases.CreateArticleUseCase, get *useCases.GetArticleUseCase, list *useCases.ListArticlesUseCase, delete *useCases.DeleteArticleUseCase, update *useCases.UpdateArticleUseCase, getUser *useCases.GetUserUseCase) *Handler {
return &Handler{
CreateUseCase: create,
GetUseCase: get,
ListUseCase: list,
DeleteUseCase: delete,
UpdateUseCase: update,
CreateUseCase: create,
GetUseCase: get,
ListUseCase: list,
DeleteUseCase: delete,
UpdateUseCase: update,
GetUserUseCase: getUser,
}
}
@@ -184,15 +186,24 @@ func (h *Handler) UnpublishArticle(w http.ResponseWriter, r *http.Request) {
h.setIsOnline(w, r, false)
}
func requestHasValidJwt(r *http.Request) bool {
token, _, err := jwtauth.FromContext(r.Context())
return token != nil && err == nil
func (h *Handler) callerIsVerified(r *http.Request) bool {
userId, ok := claims.UserId(r)
if !ok {
return false
}
currentUser, err := h.GetUserUseCase.GetUser(userId)
if err != nil {
return false
}
return currentUser.IsVerified
}
func (h *Handler) ListArticles(w http.ResponseWriter, r *http.Request) {
var articles []domain.Article
var err error
if requestHasValidJwt(r) {
if h.callerIsVerified(r) {
articles, err = h.ListUseCase.ListAllArticles()
} else {
articles, err = h.ListUseCase.ListOnlineArticles()
+148 -28
View File
@@ -2,6 +2,7 @@ package auth
import (
"RenewCMS/api/pkg/apperr"
"RenewCMS/api/pkg/claims"
validator "RenewCMS/api/pkg/validator"
"RenewCMS/internal/domain/user"
"RenewCMS/internal/infrastructure/useCases"
@@ -14,12 +15,16 @@ import (
)
type Handler struct {
Token *jwtauth.JWTAuth
GetUseCase *useCases.GetUserUseCase
ListUseCase *useCases.ListUsersUseCase
CreateUseCase *useCases.CreateUserUseCase
AuthenticateUseCase *useCases.AuthenticateUserUseCase
IsProduction bool
Token *jwtauth.JWTAuth
GetUseCase *useCases.GetUserUseCase
ListUseCase *useCases.ListUsersUseCase
CreateUseCase *useCases.CreateUserUseCase
AuthenticateUseCase *useCases.AuthenticateUserUseCase
VerifyUseCase *useCases.VerifyUserUseCase
CancelUseCase *useCases.CancelRegistrationUseCase
RequestPasswordResetUseCase *useCases.RequestPasswordResetUseCase
ResetPasswordUseCase *useCases.ResetPasswordUseCase
IsProduction bool
}
type RegisterCredentials struct {
@@ -33,6 +38,20 @@ type LoginCredentials struct {
Password string `json:"password" validate:"required,min=8"`
}
type VerifyCredentials struct {
Code string `json:"code" validate:"required"`
}
type RequestPasswordResetCredentials struct {
Email string `json:"email" validate:"required,email"`
}
type ResetPasswordCredentials struct {
Email string `json:"email" validate:"required,email"`
Code string `json:"code" validate:"required"`
Password string `json:"password" validate:"required,min=8"`
}
func (h *Handler) removeJwtCookie(w http.ResponseWriter) {
http.SetCookie(w, &http.Cookie{
Name: "jwt",
@@ -45,14 +64,18 @@ func (h *Handler) removeJwtCookie(w http.ResponseWriter) {
})
}
func NewHandler(token *jwtauth.JWTAuth, get *useCases.GetUserUseCase, list *useCases.ListUsersUseCase, create *useCases.CreateUserUseCase, authenticate *useCases.AuthenticateUserUseCase, isProduction bool) *Handler {
func NewHandler(token *jwtauth.JWTAuth, get *useCases.GetUserUseCase, list *useCases.ListUsersUseCase, create *useCases.CreateUserUseCase, authenticate *useCases.AuthenticateUserUseCase, verify *useCases.VerifyUserUseCase, cancel *useCases.CancelRegistrationUseCase, requestPasswordReset *useCases.RequestPasswordResetUseCase, resetPassword *useCases.ResetPasswordUseCase, isProduction bool) *Handler {
return &Handler{
Token: token,
GetUseCase: get,
ListUseCase: list,
CreateUseCase: create,
AuthenticateUseCase: authenticate,
IsProduction: isProduction,
Token: token,
GetUseCase: get,
ListUseCase: list,
CreateUseCase: create,
AuthenticateUseCase: authenticate,
VerifyUseCase: verify,
CancelUseCase: cancel,
RequestPasswordResetUseCase: requestPasswordReset,
ResetPasswordUseCase: resetPassword,
IsProduction: isProduction,
}
}
@@ -87,21 +110,6 @@ func (h *Handler) IsLoggedIn(r *http.Request) bool {
return token != nil && err == nil
}
func (h *Handler) IsVerified(r *http.Request) bool {
token, err := jwtauth.VerifyRequest(
h.Token,
r,
jwtauth.TokenFromCookie,
jwtauth.TokenFromHeader,
jwtauth.TokenFromQuery)
if err != nil {
return false
}
userId := token.PrivateClaims()["user_id"].(float64)
currentUser, _ := h.GetUseCase.GetUser(uint32(userId))
return currentUser.IsVerified
}
func (h *Handler) Login(w http.ResponseWriter, r *http.Request) {
var credentials LoginCredentials
@@ -163,6 +171,118 @@ func (h *Handler) Register(w http.ResponseWriter, r *http.Request) {
_, _ = w.Write(message)
}
func (h *Handler) Verify(w http.ResponseWriter, r *http.Request) {
var credentials VerifyCredentials
err := json.NewDecoder(r.Body).Decode(&credentials)
if err != nil {
http.Error(w, apperr.BodyErrorMessage, http.StatusBadRequest)
return
}
err = validator.Validate.Struct(credentials)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
userId, ok := claims.UserId(r)
if !ok {
http.Error(w, apperr.UnauthorizedErrorMessage, http.StatusUnauthorized)
return
}
verifiedUser, err := h.VerifyUseCase.Verify(userId, credentials.Code)
if err != nil {
if errors.Is(err, user.ErrUserNotFound) {
http.Error(w, apperr.UnauthorizedErrorMessage, http.StatusUnauthorized)
return
}
if errors.Is(err, user.ErrInvalidVerificationCode) || errors.Is(err, user.ErrVerificationCodeExpired) {
http.Error(w, apperr.VerificationFailedErrorMessage, http.StatusForbidden)
return
}
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
body, _ := json.Marshal(verifiedUser)
_, _ = w.Write(body)
}
func (h *Handler) CancelRegistration(w http.ResponseWriter, r *http.Request) {
userId, ok := claims.UserId(r)
if !ok {
http.Error(w, apperr.UnauthorizedErrorMessage, http.StatusUnauthorized)
return
}
err := h.CancelUseCase.CancelRegistration(userId)
if err != nil {
if errors.Is(err, user.ErrUserNotFound) {
http.Error(w, apperr.UnauthorizedErrorMessage, http.StatusUnauthorized)
return
}
if errors.Is(err, user.ErrAlreadyVerified) {
http.Error(w, apperr.AlreadyVerifiedErrorMessage, http.StatusForbidden)
return
}
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
h.removeJwtCookie(w)
message, _ := json.Marshal(map[string]any{"message": "Registration cancelled! Account deleted and HTTPonly jwt cookie cleared"})
_, _ = w.Write(message)
}
func (h *Handler) RequestPasswordReset(w http.ResponseWriter, r *http.Request) {
var credentials RequestPasswordResetCredentials
err := json.NewDecoder(r.Body).Decode(&credentials)
if err != nil {
http.Error(w, apperr.BodyErrorMessage, http.StatusBadRequest)
return
}
err = validator.Validate.Struct(credentials)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
_ = h.RequestPasswordResetUseCase.RequestPasswordReset(credentials.Email)
message, _ := json.Marshal(map[string]any{"message": apperr.PasswordResetRequestedMessage})
_, _ = w.Write(message)
}
func (h *Handler) ResetPassword(w http.ResponseWriter, r *http.Request) {
var credentials ResetPasswordCredentials
err := json.NewDecoder(r.Body).Decode(&credentials)
if err != nil {
http.Error(w, apperr.BodyErrorMessage, http.StatusBadRequest)
return
}
err = validator.Validate.Struct(credentials)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
err = h.ResetPasswordUseCase.ResetPassword(credentials.Email, credentials.Code, credentials.Password)
if err != nil {
if errors.Is(err, user.ErrInvalidPasswordResetCode) || errors.Is(err, user.ErrPasswordResetCodeExpired) {
http.Error(w, apperr.PasswordResetFailedErrorMessage, http.StatusForbidden)
return
}
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
message, _ := json.Marshal(map[string]any{"message": "Your password has been reset. You can now log in."})
_, _ = w.Write(message)
}
func (h *Handler) Logout(w http.ResponseWriter, _ *http.Request) {
h.removeJwtCookie(w)
message, _ := json.Marshal(map[string]any{"message": "User logged out! HTTPonly jwt cookie deleted"})