(registerUserInput: RegisterUserInput)
| 139 | // honest way to authorise the change. The UI hides the form for these. |
| 140 | if (!user.password) { |
| 141 | throw new BadRequestException( |
| 142 | 'This account signs in with Google and has no password to change.', |
| 143 | ); |
| 144 | } |
| 145 | if (!(await compare(currentPassword ?? '', user.password))) { |
| 146 | throw new UnauthorizedException('Current password is incorrect'); |
| 147 | } |
| 148 | if (!newPassword || newPassword.length < 8) { |
| 149 | throw new BadRequestException('Password must be at least 8 characters.'); |
| 150 | } |
| 151 | |
| 152 | user.password = await hash(newPassword, 10); |
| 153 | await this.userRepository.save(user); |
| 154 | |
| 155 | // Same reason as a reset: the point of changing a password is usually |
| 156 | // that someone else might have the old one. |
| 157 | await this.endAllSessions(userId); |
| 158 | |
| 159 | // Then re-admit this device only. |
| 160 | const accessToken = this.jwtService.sign( |
| 161 | { userId: user.id, email: user.email }, |
| 162 | { expiresIn: '30m' }, |
| 163 | ); |
| 164 | const refreshTokenEntity = await this.createRefreshToken(user); |
| 165 | this.jwtCacheService.storeAccessToken(accessToken, user.id); |
| 166 | |
| 167 | return { accessToken, refreshToken: refreshTokenEntity.token }; |
| 168 | } |
| 169 | |
| 170 | /** |
| 171 | * Start a password reset. Always answers the same thing. |
| 172 | * |
| 173 | * The response cannot depend on whether the address has an account, or on |
| 174 | * how long the work took — either one turns this into an oracle for |
| 175 | * "is X registered here". So an unknown address, a Google-only account and |
| 176 | * a real account all get the identical message, and the only thing that |
| 177 | * varies is whether an email actually goes out. |
| 178 | */ |
| 179 | async requestPasswordReset( |
| 180 | email: string, |
| 181 | ): Promise<EmailConfirmationResponse> { |
| 182 | const same = { |
| 183 | message: |
| 184 | 'If that address has an account, a reset link is on its way. Check your inbox.', |
| 185 | success: true, |
| 186 | }; |
| 187 | |
| 188 | const user = await findUserByEmail(this.userRepository, email); |
| 189 | // No account, or a Google sign-in with no password to reset. |
| 190 | if (!user || !user.password) return same; |
| 191 | |
| 192 | // Same cooldown the resend path uses, and the same column. Without it |
| 193 | // this endpoint is an unauthenticated way to send mail to any address. |
| 194 | const cooldown = 60 * 1000; |
no test coverage detected