diff --git a/openapi.yaml b/openapi.yaml index 853a18aa..5599cdaf 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -296,6 +296,40 @@ paths: '400': $ref: '#/components/responses/BadRequest' + /auth/password-reset/request: + post: + tags: + - Authentication + summary: Request a customer password reset email + description: Send an email with a password reset token to the customer's email address + operationId: requestPasswordReset + security: [] + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - customer_number + properties: + customer_number: + type: integer + description: The customer number + example: 123456 + responses: + '200': + description: Request processed + content: + application/json: + schema: + type: object + properties: + message: + type: string + '400': + $ref: '#/components/responses/BadRequest' + /auth/password-reset/validate: get: tags: diff --git a/services/nginx/app/objects/users_o.php b/services/nginx/app/objects/users_o.php index 8e1607cb..31ff1f89 100644 --- a/services/nginx/app/objects/users_o.php +++ b/services/nginx/app/objects/users_o.php @@ -157,6 +157,14 @@ class users_o extends db $this->add($customer_number, '', 0); // Nullify the password $this->password->nullify(); + // If the customer has an email address, save it + if (isset($customer_data->email)) { + $this->email->set($customer_data->email); + } + // If the customer has a name, save it as the display name + if (isset($customer_data->name)) { + $this->display_name->set($customer_data->name); + } } } // Else return false diff --git a/services/nginx/app/routes/authRoute.php b/services/nginx/app/routes/authRoute.php index 906b01d7..d23d7c60 100644 --- a/services/nginx/app/routes/authRoute.php +++ b/services/nginx/app/routes/authRoute.php @@ -4,8 +4,10 @@ namespace routes; use classes\authentication; use classes\economic; +use classes\email; use classes\recaptcha; use classes\virkdata; +use Exception; use objects\customer_password_reset_keys_o; use objects\logs_o; use objects\tokens_o; @@ -268,6 +270,50 @@ class authRoute } }); + $this->post('/auth/password-reset/request', function () { + global $response; + $this->requireRecaptcha(); + self::requireParameters(['customer_number']); + $customer_number = (int)self::getParameter('customer_number'); + + $user = (new users_o())->getUserByCustomerNumber($customer_number); + if (!$user->id) { + // For security reasons, don't reveal if the user exists + $response->success(['message' => 'If the customer exists, a password reset email has been sent.']); + } + + $email_address = $user->email->value(); + if (empty($email_address)) { + // If no email is set, we can't send the reset email + $response->error('No email address associated with this account. Please contact support.', 400); + } + + // Generate token + $token = customer_password_reset_keys_o::generateToken(); + + // Save token + $reset_key_o = new customer_password_reset_keys_o(); + $reset_key_o->add([ + 'customer_id' => $customer_number, + 'token' => $token, + 'note' => 'Requested via API' + ]); + + // Send email + $email = new email(); + $reset_link = "https://truckwash.io/auth/password-reset/" . $token; + + $subject = 'Adgangskode nulstilling'; + $message = "Du har anmodet om at nulstille din adgangskode. Klik på linket herunder for at fortsætte:

$reset_link

Linket er gyldigt i 1 time."; + + try { + $email->sendEmail($email_address, $user->display_name->value() ?? 'Kunde', $subject, $message, null); + $response->success(['message' => 'If the customer exists, a password reset email has been sent.']); + } catch (Exception $e) { + $response->error('Failed to send email: ' . $e->getMessage(), 500); + } + }); + $this->get('/auth/password-reset/validate', function () { global $response; self::requireParameters(['token']);