2025-12-03 19:29:37 +00:00
package handlers
import (
2025-12-29 18:23:23 +00:00
"context"
2025-12-03 19:29:37 +00:00
"net/http"
2026-04-16 12:52:46 +00:00
"os"
"strings"
2025-12-03 19:29:37 +00:00
"veza-backend-api/internal/core/auth" // Added import for authcore
"veza-backend-api/internal/services"
"github.com/gin-gonic/gin"
2025-12-24 11:27:39 +00:00
"github.com/google/uuid"
2025-12-03 19:29:37 +00:00
"go.uber.org/zap"
)
2026-04-16 12:52:46 +00:00
// isProductionEnv reports whether APP_ENV is set to "production". Used to
// decide whether SMTP delivery failures should surface as HTTP 500.
func isProductionEnv ( ) bool {
return strings . EqualFold ( os . Getenv ( "APP_ENV" ) , "production" )
}
2025-12-03 19:29:37 +00:00
// RequestPasswordResetRequest represents a request to reset password
// T0193: Request structure for password reset endpoint
2025-12-16 18:34:08 +00:00
// MOD-P1-001: Ajout tags validate pour validation systématique
2025-12-03 19:29:37 +00:00
type RequestPasswordResetRequest struct {
2025-12-16 18:34:08 +00:00
Email string ` json:"email" binding:"required,email" validate:"required,email" `
2025-12-03 19:29:37 +00:00
}
2025-12-29 18:23:23 +00:00
// PasswordResetServiceInterface defines methods needed for password reset handler
type PasswordResetServiceInterface interface {
GenerateToken ( ) ( string , error )
StoreToken ( userID uuid . UUID , token string ) error
VerifyToken ( token string ) ( uuid . UUID , error )
MarkTokenAsUsed ( token string ) error
InvalidateOldTokens ( userID uuid . UUID ) error
}
// PasswordServiceInterface defines methods needed for password operations
type PasswordServiceInterface interface {
GetUserByEmail ( email string ) ( * services . UserInfo , error )
ValidatePassword ( password string ) error
UpdatePassword ( userID uuid . UUID , password string ) error
}
// EmailServiceInterface defines methods needed for email operations
type EmailServiceInterface interface {
SendPasswordResetEmail ( userID uuid . UUID , email , token string ) error
}
// AuditServiceInterface defines methods needed for audit operations
type AuditServiceInterface interface {
LogPasswordResetRequest ( ctx context . Context , userID * uuid . UUID , email , ip , userAgent string ) error
LogPasswordReset ( ctx context . Context , userID uuid . UUID , success bool , ip , userAgent string ) error
}
// SessionServiceInterface defines methods needed for session operations
type SessionServiceInterface interface {
RevokeAllUserSessions ( ctx context . Context , userID uuid . UUID ) ( int64 , error )
}
// AuthServiceInterface defines methods needed for auth operations
type AuthServiceInterface interface {
InvalidateAllUserSessions ( ctx context . Context , userID uuid . UUID , sessionService SessionServiceInterface ) error
}
2025-12-03 19:29:37 +00:00
// RequestPasswordReset handles password reset request
// T0193: Creates endpoint POST /api/v1/auth/password/reset-request
2025-12-24 11:27:39 +00:00
// BE-SEC-013: Added audit logging for password reset requests
feat(openapi): annotate queue + password-reset handlers + regen
Closes the two annotation gaps that blocked finishing the orval
migration in v1.0.8 :
- queue_handler.go (5 routes — GetQueue, UpdateQueue, AddQueueItem,
RemoveQueueItem, ClearQueue) — under @Tags Queue with @Security
BearerAuth, @Param body/path, @Success/@Failure on the standard
APIResponse envelope.
- queue_session_handler.go (5 routes — CreateSession, GetSession,
DeleteSession, AddToSession, RemoveFromSession). GetSession is
public (no @Security tag) since the share-token URL is meant for
join-via-link from outside the auth wall.
- password_reset_handler.go (2 routes — RequestPasswordReset and
ResetPassword factory functions). Both are public (no @Security)
since they're the entry-points for users who can't log in. The
request-side annotation documents the intentional generic 200
response (anti-enumeration: same body whether the email exists or
not).
After regen :
- openapi.yaml gains 7 queue paths (/queue, /queue/items[/{id}],
/queue/session[/{token}[/items[/{id}]]]) and 2 password paths
(/auth/password/reset, /auth/password/reset-request). +568 LOC.
- docs/{docs.go,swagger.json,swagger.yaml} updated identically by
swag init.
- apps/web/src/services/generated/queue/queue.ts created (10
HTTP funcs + matching React Query hooks). model/ index extended
with the queue + password-reset request/response shapes.
Validates with `swag init` (Swagger 2.0). go build ./... clean. No
runtime behaviour change — annotations are pure metadata read by the
spec generator. The orval regen IS the wiring point for the
follow-up frontend commit (queue.ts migration + authService finish).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 22:55:26 +00:00
// @Summary Request password reset
// @Description Sends a password reset link to the user's email if the address exists. Always returns 200 with a generic message to prevent email enumeration.
// @Tags Auth
// @Accept json
// @Produce json
// @Param request body RequestPasswordResetRequest true "Email of the account to reset"
// @Success 200 {object} APIResponse{data=object{message=string}} "If the email exists, a reset link has been sent"
// @Failure 400 {object} APIResponse "Validation error"
// @Failure 500 {object} APIResponse "Token generation/storage failed (or SMTP failure in production)"
// @Router /auth/password/reset-request [post]
2025-12-03 19:29:37 +00:00
func RequestPasswordReset (
passwordResetService * services . PasswordResetService ,
passwordService * services . PasswordService ,
emailService * services . EmailService ,
2025-12-24 11:27:39 +00:00
auditService * services . AuditService ,
2025-12-03 19:29:37 +00:00
logger * zap . Logger ,
2025-12-29 18:23:23 +00:00
) gin . HandlerFunc {
return RequestPasswordResetWithInterfaces (
passwordResetService ,
passwordService ,
emailService ,
auditService ,
logger ,
)
}
// RequestPasswordResetWithInterfaces handles password reset request with interfaces (for testing)
func RequestPasswordResetWithInterfaces (
passwordResetService PasswordResetServiceInterface ,
passwordService PasswordServiceInterface ,
emailService EmailServiceInterface ,
auditService AuditServiceInterface ,
logger * zap . Logger ,
2025-12-03 19:29:37 +00:00
) gin . HandlerFunc {
return func ( c * gin . Context ) {
P0: stabilisation backend/chat/stream + nouvelle base migrations v1
Backend Go:
- Remplacement complet des anciennes migrations par la base V1 alignée sur ORIGIN.
- Durcissement global du parsing JSON (BindAndValidateJSON + RespondWithAppError).
- Sécurisation de config.go, CORS, statuts de santé et monitoring.
- Implémentation des transactions P0 (RBAC, duplication de playlists, social toggles).
- Ajout d’un job worker structuré (emails, analytics, thumbnails) + tests associés.
- Nouvelle doc backend : AUDIT_CONFIG, BACKEND_CONFIG, AUTH_PASSWORD_RESET, JOB_WORKER_*.
Chat server (Rust):
- Refonte du pipeline JWT + sécurité, audit et rate limiting avancé.
- Implémentation complète du cycle de message (read receipts, delivered, edit/delete, typing).
- Nettoyage des panics, gestion d’erreurs robuste, logs structurés.
- Migrations chat alignées sur le schéma UUID et nouvelles features.
Stream server (Rust):
- Refonte du moteur de streaming (encoding pipeline + HLS) et des modules core.
- Transactions P0 pour les jobs et segments, garanties d’atomicité.
- Documentation détaillée de la pipeline (AUDIT_STREAM_*, DESIGN_STREAM_PIPELINE, TRANSACTIONS_P0_IMPLEMENTATION).
Documentation & audits:
- TRIAGE.md et AUDIT_STABILITY.md à jour avec l’état réel des 3 services.
- Cartographie complète des migrations et des transactions (DB_MIGRATIONS_*, DB_TRANSACTION_PLAN, AUDIT_DB_TRANSACTIONS, TRANSACTION_TESTS_PHASE3).
- Scripts de reset et de cleanup pour la lab DB et la V1.
Ce commit fige l’ensemble du travail de stabilisation P0 (UUID, backend, chat et stream) avant les phases suivantes (Coherence Guardian, WS hardening, etc.).
2025-12-06 10:14:38 +00:00
commonHandler := NewCommonHandler ( logger )
2025-12-03 19:29:37 +00:00
var req RequestPasswordResetRequest
P0: stabilisation backend/chat/stream + nouvelle base migrations v1
Backend Go:
- Remplacement complet des anciennes migrations par la base V1 alignée sur ORIGIN.
- Durcissement global du parsing JSON (BindAndValidateJSON + RespondWithAppError).
- Sécurisation de config.go, CORS, statuts de santé et monitoring.
- Implémentation des transactions P0 (RBAC, duplication de playlists, social toggles).
- Ajout d’un job worker structuré (emails, analytics, thumbnails) + tests associés.
- Nouvelle doc backend : AUDIT_CONFIG, BACKEND_CONFIG, AUTH_PASSWORD_RESET, JOB_WORKER_*.
Chat server (Rust):
- Refonte du pipeline JWT + sécurité, audit et rate limiting avancé.
- Implémentation complète du cycle de message (read receipts, delivered, edit/delete, typing).
- Nettoyage des panics, gestion d’erreurs robuste, logs structurés.
- Migrations chat alignées sur le schéma UUID et nouvelles features.
Stream server (Rust):
- Refonte du moteur de streaming (encoding pipeline + HLS) et des modules core.
- Transactions P0 pour les jobs et segments, garanties d’atomicité.
- Documentation détaillée de la pipeline (AUDIT_STREAM_*, DESIGN_STREAM_PIPELINE, TRANSACTIONS_P0_IMPLEMENTATION).
Documentation & audits:
- TRIAGE.md et AUDIT_STABILITY.md à jour avec l’état réel des 3 services.
- Cartographie complète des migrations et des transactions (DB_MIGRATIONS_*, DB_TRANSACTION_PLAN, AUDIT_DB_TRANSACTIONS, TRANSACTION_TESTS_PHASE3).
- Scripts de reset et de cleanup pour la lab DB et la V1.
Ce commit fige l’ensemble du travail de stabilisation P0 (UUID, backend, chat et stream) avant les phases suivantes (Coherence Guardian, WS hardening, etc.).
2025-12-06 10:14:38 +00:00
if appErr := commonHandler . BindAndValidateJSON ( c , & req ) ; appErr != nil {
RespondWithAppError ( c , appErr )
2025-12-03 19:29:37 +00:00
return
}
// Find user by email
user , err := passwordService . GetUserByEmail ( req . Email )
if err != nil {
// Always return success for security (prevent email enumeration)
2025-12-06 16:21:59 +00:00
RespondSuccess ( c , http . StatusOK , gin . H { "message" : "If the email exists, a reset link has been sent" } )
2025-12-03 19:29:37 +00:00
return
}
// Invalidate old tokens
if err := passwordResetService . InvalidateOldTokens ( user . ID ) ; err != nil {
logger . Error ( "Failed to invalidate old tokens" ,
zap . String ( "user_id" , user . ID . String ( ) ) ,
zap . Error ( err ) ,
)
// Continue anyway, not critical
}
// Generate token
token , err := passwordResetService . GenerateToken ( )
if err != nil {
logger . Error ( "Failed to generate password reset token" ,
zap . String ( "user_id" , user . ID . String ( ) ) ,
zap . Error ( err ) ,
)
c . JSON ( http . StatusInternalServerError , gin . H { "error" : "failed to generate token" } )
return
}
// Store token
if err := passwordResetService . StoreToken ( user . ID , token ) ; err != nil {
logger . Error ( "Failed to store password reset token" ,
zap . String ( "user_id" , user . ID . String ( ) ) ,
zap . Error ( err ) ,
)
c . JSON ( http . StatusInternalServerError , gin . H { "error" : "failed to store token" } )
return
}
2026-04-16 12:52:46 +00:00
// Send email. In production SMTP must work — a silent log-only failure
// would leave the user stuck with no way to reset. In development we
// keep the generic success response so local sign-ups keep flowing.
2025-12-03 19:29:37 +00:00
if err := emailService . SendPasswordResetEmail ( user . ID , user . Email , token ) ; err != nil {
logger . Error ( "Failed to send password reset email" ,
zap . String ( "user_id" , user . ID . String ( ) ) ,
zap . String ( "email" , user . Email ) ,
zap . Error ( err ) ,
)
2026-04-16 12:52:46 +00:00
if isProductionEnv ( ) {
c . JSON ( http . StatusInternalServerError , gin . H { "error" : "failed to send password reset email" } )
return
}
2025-12-03 19:29:37 +00:00
}
2025-12-24 11:27:39 +00:00
// BE-SEC-013: Log password reset request
if auditService != nil {
userID := user . ID
if err := auditService . LogPasswordResetRequest ( c . Request . Context ( ) , & userID , user . Email , c . ClientIP ( ) , c . GetHeader ( "User-Agent" ) ) ; err != nil {
logger . Warn ( "Failed to log password reset request" ,
zap . String ( "user_id" , user . ID . String ( ) ) ,
zap . Error ( err ) ,
)
}
}
2025-12-03 19:29:37 +00:00
// Always return generic success message for security
2025-12-06 16:21:59 +00:00
RespondSuccess ( c , http . StatusOK , gin . H { "message" : "If the email exists, a reset link has been sent" } )
2025-12-03 19:29:37 +00:00
}
}
// ResetPasswordRequest represents a request to complete password reset
// T0194: Request structure for password reset completion
2025-12-16 18:34:08 +00:00
// MOD-P1-001: Ajout tags validate pour validation systématique
2025-12-03 19:29:37 +00:00
type ResetPasswordRequest struct {
2025-12-16 18:34:08 +00:00
Token string ` json:"token" binding:"required" validate:"required" `
chore: consolidate CI, E2E, backend and frontend updates
- CI: workflows updates (cd, ci), remove playwright.yml
- E2E: global-setup, auth/playlists/profile specs
- Remove playwright-report and test-results artifacts from tracking
- Backend: auth, handlers, services, workers, migrations
- Frontend: components, features, vite config
- Add e2e-results.json to gitignore
- Docs: REMEDIATION_PROGRESS, audit archive
- Rust: chat-server, stream-server updates
2026-02-17 15:43:21 +00:00
NewPassword string ` json:"new_password" binding:"required,min=12" validate:"required,min=12" `
2025-12-03 19:29:37 +00:00
}
// ResetPassword handles password reset completion
// T0194: Creates endpoint POST /api/v1/auth/password/reset
// T0200: Uses AuthService.InvalidateAllUserSessions to invalidate sessions and update token_version
2025-12-24 11:27:39 +00:00
// BE-SEC-013: Added audit logging for password reset completion
feat(openapi): annotate queue + password-reset handlers + regen
Closes the two annotation gaps that blocked finishing the orval
migration in v1.0.8 :
- queue_handler.go (5 routes — GetQueue, UpdateQueue, AddQueueItem,
RemoveQueueItem, ClearQueue) — under @Tags Queue with @Security
BearerAuth, @Param body/path, @Success/@Failure on the standard
APIResponse envelope.
- queue_session_handler.go (5 routes — CreateSession, GetSession,
DeleteSession, AddToSession, RemoveFromSession). GetSession is
public (no @Security tag) since the share-token URL is meant for
join-via-link from outside the auth wall.
- password_reset_handler.go (2 routes — RequestPasswordReset and
ResetPassword factory functions). Both are public (no @Security)
since they're the entry-points for users who can't log in. The
request-side annotation documents the intentional generic 200
response (anti-enumeration: same body whether the email exists or
not).
After regen :
- openapi.yaml gains 7 queue paths (/queue, /queue/items[/{id}],
/queue/session[/{token}[/items[/{id}]]]) and 2 password paths
(/auth/password/reset, /auth/password/reset-request). +568 LOC.
- docs/{docs.go,swagger.json,swagger.yaml} updated identically by
swag init.
- apps/web/src/services/generated/queue/queue.ts created (10
HTTP funcs + matching React Query hooks). model/ index extended
with the queue + password-reset request/response shapes.
Validates with `swag init` (Swagger 2.0). go build ./... clean. No
runtime behaviour change — annotations are pure metadata read by the
spec generator. The orval regen IS the wiring point for the
follow-up frontend commit (queue.ts migration + authService finish).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 22:55:26 +00:00
// @Summary Reset password with token
// @Description Completes a password reset using a valid token previously emailed to the user. Invalidates all the user's existing sessions on success.
// @Tags Auth
// @Accept json
// @Produce json
// @Param request body ResetPasswordRequest true "Reset token + new password"
// @Success 200 {object} APIResponse{data=object{message=string}} "Password reset successfully"
// @Failure 400 {object} APIResponse "Invalid or expired token, or password validation failed"
// @Failure 500 {object} APIResponse "Failed to update password"
// @Router /auth/password/reset [post]
2025-12-03 19:29:37 +00:00
func ResetPassword (
passwordResetService * services . PasswordResetService ,
passwordService * services . PasswordService ,
authService * auth . AuthService , // Changed to *auth.AuthService
sessionService * services . SessionService ,
2025-12-24 11:27:39 +00:00
auditService * services . AuditService ,
2025-12-03 19:29:37 +00:00
logger * zap . Logger ,
2025-12-29 18:23:23 +00:00
) gin . HandlerFunc {
// Convert concrete types to interfaces
var authServiceInterface AuthServiceInterface
if authService != nil {
authServiceInterface = & authServiceAdapter { authService : authService }
}
var sessionServiceInterface SessionServiceInterface
if sessionService != nil {
sessionServiceInterface = & sessionServiceAdapter { sessionService : sessionService }
}
return ResetPasswordWithInterfaces (
passwordResetService ,
passwordService ,
authServiceInterface ,
sessionServiceInterface ,
auditService ,
logger ,
)
}
// authServiceAdapter adapts *auth.AuthService to AuthServiceInterface
type authServiceAdapter struct {
authService * auth . AuthService
}
func ( a * authServiceAdapter ) InvalidateAllUserSessions ( ctx context . Context , userID uuid . UUID , sessionService SessionServiceInterface ) error {
return a . authService . InvalidateAllUserSessions ( ctx , userID , sessionService )
}
// sessionServiceAdapter adapts *services.SessionService to SessionServiceInterface
type sessionServiceAdapter struct {
sessionService * services . SessionService
}
func ( s * sessionServiceAdapter ) RevokeAllUserSessions ( ctx context . Context , userID uuid . UUID ) ( int64 , error ) {
return s . sessionService . RevokeAllUserSessions ( ctx , userID )
}
// ResetPasswordWithInterfaces handles password reset completion with interfaces (for testing)
func ResetPasswordWithInterfaces (
passwordResetService PasswordResetServiceInterface ,
passwordService PasswordServiceInterface ,
authService AuthServiceInterface ,
sessionService SessionServiceInterface ,
auditService AuditServiceInterface ,
logger * zap . Logger ,
2025-12-03 19:29:37 +00:00
) gin . HandlerFunc {
return func ( c * gin . Context ) {
P0: stabilisation backend/chat/stream + nouvelle base migrations v1
Backend Go:
- Remplacement complet des anciennes migrations par la base V1 alignée sur ORIGIN.
- Durcissement global du parsing JSON (BindAndValidateJSON + RespondWithAppError).
- Sécurisation de config.go, CORS, statuts de santé et monitoring.
- Implémentation des transactions P0 (RBAC, duplication de playlists, social toggles).
- Ajout d’un job worker structuré (emails, analytics, thumbnails) + tests associés.
- Nouvelle doc backend : AUDIT_CONFIG, BACKEND_CONFIG, AUTH_PASSWORD_RESET, JOB_WORKER_*.
Chat server (Rust):
- Refonte du pipeline JWT + sécurité, audit et rate limiting avancé.
- Implémentation complète du cycle de message (read receipts, delivered, edit/delete, typing).
- Nettoyage des panics, gestion d’erreurs robuste, logs structurés.
- Migrations chat alignées sur le schéma UUID et nouvelles features.
Stream server (Rust):
- Refonte du moteur de streaming (encoding pipeline + HLS) et des modules core.
- Transactions P0 pour les jobs et segments, garanties d’atomicité.
- Documentation détaillée de la pipeline (AUDIT_STREAM_*, DESIGN_STREAM_PIPELINE, TRANSACTIONS_P0_IMPLEMENTATION).
Documentation & audits:
- TRIAGE.md et AUDIT_STABILITY.md à jour avec l’état réel des 3 services.
- Cartographie complète des migrations et des transactions (DB_MIGRATIONS_*, DB_TRANSACTION_PLAN, AUDIT_DB_TRANSACTIONS, TRANSACTION_TESTS_PHASE3).
- Scripts de reset et de cleanup pour la lab DB et la V1.
Ce commit fige l’ensemble du travail de stabilisation P0 (UUID, backend, chat et stream) avant les phases suivantes (Coherence Guardian, WS hardening, etc.).
2025-12-06 10:14:38 +00:00
commonHandler := NewCommonHandler ( logger )
2025-12-03 19:29:37 +00:00
var req ResetPasswordRequest
P0: stabilisation backend/chat/stream + nouvelle base migrations v1
Backend Go:
- Remplacement complet des anciennes migrations par la base V1 alignée sur ORIGIN.
- Durcissement global du parsing JSON (BindAndValidateJSON + RespondWithAppError).
- Sécurisation de config.go, CORS, statuts de santé et monitoring.
- Implémentation des transactions P0 (RBAC, duplication de playlists, social toggles).
- Ajout d’un job worker structuré (emails, analytics, thumbnails) + tests associés.
- Nouvelle doc backend : AUDIT_CONFIG, BACKEND_CONFIG, AUTH_PASSWORD_RESET, JOB_WORKER_*.
Chat server (Rust):
- Refonte du pipeline JWT + sécurité, audit et rate limiting avancé.
- Implémentation complète du cycle de message (read receipts, delivered, edit/delete, typing).
- Nettoyage des panics, gestion d’erreurs robuste, logs structurés.
- Migrations chat alignées sur le schéma UUID et nouvelles features.
Stream server (Rust):
- Refonte du moteur de streaming (encoding pipeline + HLS) et des modules core.
- Transactions P0 pour les jobs et segments, garanties d’atomicité.
- Documentation détaillée de la pipeline (AUDIT_STREAM_*, DESIGN_STREAM_PIPELINE, TRANSACTIONS_P0_IMPLEMENTATION).
Documentation & audits:
- TRIAGE.md et AUDIT_STABILITY.md à jour avec l’état réel des 3 services.
- Cartographie complète des migrations et des transactions (DB_MIGRATIONS_*, DB_TRANSACTION_PLAN, AUDIT_DB_TRANSACTIONS, TRANSACTION_TESTS_PHASE3).
- Scripts de reset et de cleanup pour la lab DB et la V1.
Ce commit fige l’ensemble du travail de stabilisation P0 (UUID, backend, chat et stream) avant les phases suivantes (Coherence Guardian, WS hardening, etc.).
2025-12-06 10:14:38 +00:00
if appErr := commonHandler . BindAndValidateJSON ( c , & req ) ; appErr != nil {
RespondWithAppError ( c , appErr )
2025-12-03 19:29:37 +00:00
return
}
// Verify token
userID , err := passwordResetService . VerifyToken ( req . Token )
if err != nil {
logger . Warn ( "Password reset token verification failed" ,
zap . String ( "token" , req . Token [ : min ( len ( req . Token ) , 8 ) ] + "..." ) ,
zap . Error ( err ) ,
)
2025-12-24 11:27:39 +00:00
// BE-SEC-013: Log failed password reset attempt
if auditService != nil {
if err := auditService . LogPasswordReset ( c . Request . Context ( ) , uuid . Nil , false , c . ClientIP ( ) , c . GetHeader ( "User-Agent" ) ) ; err != nil {
logger . Warn ( "Failed to log password reset failure" , zap . Error ( err ) )
}
}
2025-12-03 19:29:37 +00:00
c . JSON ( http . StatusBadRequest , gin . H { "error" : "invalid or expired token" } )
return
}
// Validate password strength
if err := passwordService . ValidatePassword ( req . NewPassword ) ; err != nil {
logger . Warn ( "Password validation failed" ,
zap . String ( "user_id" , userID . String ( ) ) ,
zap . Error ( err ) ,
)
c . JSON ( http . StatusBadRequest , gin . H { "error" : err . Error ( ) } )
return
}
// Update password
if err := passwordService . UpdatePassword ( userID , req . NewPassword ) ; err != nil {
logger . Error ( "Failed to update password" ,
zap . String ( "user_id" , userID . String ( ) ) ,
zap . Error ( err ) ,
)
c . JSON ( http . StatusInternalServerError , gin . H { "error" : "failed to update password" } )
return
}
// Mark token as used
if err := passwordResetService . MarkTokenAsUsed ( req . Token ) ; err != nil {
// Log but don't fail - password is already updated
logger . Warn ( "Failed to mark token as used" ,
zap . String ( "user_id" , userID . String ( ) ) ,
zap . String ( "token" , req . Token [ : min ( len ( req . Token ) , 8 ) ] + "..." ) ,
zap . Error ( err ) ,
)
}
// T0200: Invalidate all user sessions via AuthService
// This updates token_version and revokes all sessions
2025-12-29 18:23:23 +00:00
if authService != nil && sessionService != nil {
2025-12-03 19:29:37 +00:00
err := authService . InvalidateAllUserSessions ( c . Request . Context ( ) , userID , sessionService )
if err != nil {
// Log but don't fail - password is already updated
logger . Warn ( "Failed to invalidate user sessions" ,
zap . String ( "user_id" , userID . String ( ) ) ,
zap . Error ( err ) ,
)
} else {
logger . Info ( "User sessions invalidated after password reset" ,
zap . String ( "user_id" , userID . String ( ) ) ,
)
}
}
logger . Info ( "Password reset completed successfully" ,
zap . String ( "user_id" , userID . String ( ) ) ,
)
2025-12-24 11:27:39 +00:00
// BE-SEC-013: Log successful password reset
if auditService != nil {
if err := auditService . LogPasswordReset ( c . Request . Context ( ) , userID , true , c . ClientIP ( ) , c . GetHeader ( "User-Agent" ) ) ; err != nil {
logger . Warn ( "Failed to log password reset success" ,
zap . String ( "user_id" , userID . String ( ) ) ,
zap . Error ( err ) ,
)
}
}
2025-12-06 16:21:59 +00:00
RespondSuccess ( c , http . StatusOK , gin . H { "message" : "Password reset successfully" } )
2025-12-03 19:29:37 +00:00
}
}
// min returns the minimum of two integers (helper function)
func min ( a , b int ) int {
if a < b {
return a
}
return b
}