fix: resetting the password or cancelling a registration ends existing sessions: a token issued before the change is refused

This commit is contained in:
fsylvain committed 2026-09-25 05:03:34 +02:00
1 parent 81c4b62175
commit f3ec3d2fa2
15 files changed
+844 -13

No files matched your search

+4 -4
View File
@@ -99,8 +99,8 @@ func NewHandler(token *jwtauth.JWTAuth, get *useCases.GetUserUseCase, list *useC
}
}
func (h *Handler) SetJwtCookie(w *http.ResponseWriter, userId uint32) error {
_, tokenString, err := h.Token.Encode(map[string]any{"user_id": userId})
func (h *Handler) SetJwtCookie(w *http.ResponseWriter, u user.User) error {
_, tokenString, err := h.Token.Encode(map[string]any{"user_id": u.ID, "token_version": u.TokenVersion})
if err != nil {
return err
}
@@ -151,7 +151,7 @@ func (h *Handler) Login(w http.ResponseWriter, r *http.Request) {
return
}
_ = h.SetJwtCookie(&w, dbUser.ID)
_ = h.SetJwtCookie(&w, dbUser)
message, _ := json.Marshal(map[string]any{"message": "User logged in! HTTPonly jwt cookie created"})
_, _ = w.Write(message)
@@ -185,7 +185,7 @@ func (h *Handler) Register(w http.ResponseWriter, r *http.Request) {
return
}
_ = h.SetJwtCookie(&w, createdUser.ID)
_ = h.SetJwtCookie(&w, createdUser)
message, _ := json.Marshal(map[string]any{"message": "User registered! HTTPonly jwt cookie created"})
_, _ = w.Write(message)
+42 -9
View File
@@ -16,22 +16,55 @@ func JsonContentTypeMiddleware(next http.Handler) http.Handler {
})
}
func RequireVerifiedUser(getUser *useCases.GetUserUseCase) func(http.Handler) http.Handler {
func resolveCurrentUser(getUser *useCases.GetUserUseCase, w http.ResponseWriter, r *http.Request) (user.User, bool) {
userId, ok := claims.UserId(r)
if !ok {
http.Error(w, apperr.UnauthorizedErrorMessage, http.StatusUnauthorized)
return user.User{}, false
}
currentUser, err := getUser.GetUser(userId)
if err != nil {
if errors.Is(err, user.ErrUserNotFound) {
http.Error(w, apperr.UnauthorizedErrorMessage, http.StatusUnauthorized)
return user.User{}, false
}
http.Error(w, "Internal server error", http.StatusInternalServerError)
return user.User{}, false
}
return currentUser, true
}
func RequireCurrentToken(getUser *useCases.GetUserUseCase) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
userId, ok := claims.UserId(r)
currentUser, ok := resolveCurrentUser(getUser, w, r)
if !ok {
return
}
tokenVersion, ok := claims.TokenVersion(r)
if !ok {
http.Error(w, apperr.UnauthorizedErrorMessage, http.StatusUnauthorized)
return
}
currentUser, err := getUser.GetUser(userId)
if err != nil {
if errors.Is(err, user.ErrUserNotFound) {
http.Error(w, apperr.UnauthorizedErrorMessage, http.StatusUnauthorized)
return
}
http.Error(w, "Internal server error", http.StatusInternalServerError)
if tokenVersion != currentUser.TokenVersion {
http.Error(w, apperr.UnauthorizedErrorMessage, http.StatusUnauthorized)
return
}
next.ServeHTTP(w, r)
})
}
}
func RequireVerifiedUser(getUser *useCases.GetUserUseCase) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
currentUser, ok := resolveCurrentUser(getUser, w, r)
if !ok {
return
}
+19
View File
@@ -24,3 +24,22 @@ func UserId(r *http.Request) (uint32, bool) {
return uint32(userIdFloat), true
}
func TokenVersion(r *http.Request) (uint32, bool) {
_, claims, err := jwtauth.FromContext(r.Context())
if err != nil {
return 0, false
}
rawTokenVersion, ok := claims["token_version"]
if !ok {
return 0, false
}
tokenVersionFloat, ok := rawTokenVersion.(float64)
if !ok {
return 0, false
}
return uint32(tokenVersionFloat), true
}
+2
View File
@@ -40,6 +40,7 @@ func NewArticleRouter(container *UseCases) http.Handler {
})
r.Group(func(r chi.Router) {
r.Use(jwtauth.Authenticator(container.Token))
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
r.Post("/", articleHandler.PostArticle)
r.Delete("/{id}", articleHandler.DeleteArticle)
@@ -64,6 +65,7 @@ func NewAuthRouter(container *UseCases, isProduction bool) http.Handler {
r.Group(func(r chi.Router) {
r.Use(jwtauth.Verifier(container.Token))
r.Use(jwtauth.Authenticator(container.Token))
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
r.Post("/verify", authHandler.Verify)
r.Delete("/register", authHandler.CancelRegistration)
})