fix: resetting the password or cancelling a registration ends existing sessions: a token issued before the change is refused
This commit is contained in:
1 parent
81c4b62175
commit
f3ec3d2fa2
15 files changed
+844
-13
No files matched your search
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
})
|
||||
|
||||
Reference in new issue
Block a user