fix(api): reissue JWT with mustChangePassword=false after password change
Tessera CI/CD / Lint & Type Check (push) Successful in 39s
Tessera CI/CD / Tests (push) Successful in 36s
Tessera CI/CD / Build & Publish Images (push) Successful in 23s

After a successful password change the old cookie still contained
mustChangePassword=true, causing the middleware to redirect back to
/change-password. Now changePassword issues a fresh session cookie.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-29 16:09:36 +02:00
parent b28ee472c5
commit 5779f0f6c9
2 changed files with 20 additions and 6 deletions
+2
View File
@@ -97,11 +97,13 @@ export class AuthController {
async changePassword(
@CurrentUser() user: any,
@Body() dto: ChangePasswordDto,
@Res({ passthrough: true }) res: Response,
) {
await this.authService.changePassword(
user.id,
dto.currentPassword,
dto.newPassword,
res,
);
return { message: 'Password changed successfully.' };
}
+18 -6
View File
@@ -190,6 +190,7 @@ export class AuthService {
userId: string,
currentPassword: string,
newPassword: string,
response: Response,
): Promise<void> {
const user = await this.prisma.user.findUnique({
where: { id: userId },
@@ -199,20 +200,31 @@ export class AuthService {
throw new UnauthorizedException('User not found or has no local password');
}
// Verify current password
const isValid = await argon2.verify(user.passwordHash, currentPassword);
if (!isValid) {
throw new UnauthorizedException('Current password is incorrect');
}
// Hash new password and update
const passwordHash = await argon2.hash(newPassword);
await this.prisma.user.update({
where: { id: userId },
data: {
passwordHash,
mustChangePassword: false,
},
data: { passwordHash, mustChangePassword: false },
});
const payload = {
sub: user.id,
username: user.username,
role: user.role,
tenantId: user.tenantId,
mustChangePassword: false,
};
const token = this.jwtService.sign(payload);
(response as any).cookie('session', token, {
httpOnly: true,
secure: this.configService.get('NODE_ENV') === 'production',
sameSite: 'lax',
maxAge: 30 * 24 * 60 * 60 * 1000,
path: '/',
});
this.logger.log(`Password changed for user ${userId}`);