(loginUserInput: LoginUserInput)
| 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; |
| 195 | if ( |
| 196 | user.lastEmailSendTime && |
| 197 | Date.now() - user.lastEmailSendTime.getTime() < cooldown |
| 198 | ) { |
| 199 | return same; |
| 200 | } |
| 201 | |
| 202 | const token = signResetToken( |
| 203 | this.configService.jwtSecret, |
| 204 | user.id, |
| 205 | user.password, |
| 206 | ); |
| 207 | |
| 208 | user.lastEmailSendTime = new Date(); |
| 209 | await this.userRepository.save(user); |
| 210 | |
| 211 | if (this.isMailEnabled) { |
| 212 | await this.mailService.sendPasswordResetEmail(user, token); |
| 213 | } else { |
| 214 | // SMTP is off in every environment today, so without this the feature |
| 215 | // could not be exercised at all. The link is the whole secret, so it |
| 216 | // goes to the server log and nowhere near the response. |
| 217 | Logger.warn( |
| 218 | `[auth] mail disabled — reset link for ${user.email}: ` + |
| 219 | `${this.configService.frontendUrl}/reset-password?token=${token}`, |
| 220 | ); |
| 221 | } |
| 222 | |
| 223 | return same; |
| 224 | } |
| 225 | |
| 226 | /** |
| 227 | * Finish a reset. |
| 228 | * |
| 229 | * Single use falls out of how the token is signed: the key includes the |
nothing calls this directly
no test coverage detected