diff --git a/app/login/api/engine/onboarding.php b/app/login/api/engine/onboarding.php index 9a6c465..433f2a6 100644 --- a/app/login/api/engine/onboarding.php +++ b/app/login/api/engine/onboarding.php @@ -19,8 +19,10 @@ * 2. CSRF check — rejects requests missing a valid X-CSRF-Token header. * 3. Decode and sanitise input fields. * 4. Required field validation — company_name and channel_name must be non-empty. - * 5. Required SMTP validation — smtp_host, smtp_username, smtp_password - * must all be provided (company SMTP is mandatory for WMS email delivery). + * 5. SMTP validation — smtp_host, smtp_username, smtp_password must all be + * provided while email OTP is on (company SMTP delivers the OTP). With + * OTP_REQUIRED=false they are optional but all-or-nothing: left blank, + * steps 6-8 and 13 are skipped and the company is created without SMTP. * 6. Normalise smtp_port to one of ['25', '465', '587'] (default: 587). * Normalise smtp_encryption to one of ['tls', 'ssl', 'none'] (default: tls). * 7. Encrypt SMTP password with OpenSSL (same method/iv/key as rest of app). @@ -52,6 +54,7 @@ require_once '../../../session.php'; require_once '../../../config.php'; require_once '../../../dbconn.php'; require_once '../../../assets/utils/db_helpers.php'; +require_once '../../../assets/utils/otp_policy.php'; header('Content-Type: application/json; charset=utf-8'); @@ -114,59 +117,67 @@ try { } // ── Step 5: SMTP field validation ──────────────────────────────────────── - // SMTP is mandatory because the company needs to send OTP emails to users. - // An account without working SMTP would be unable to complete 2FA login. + // While email OTP is on, SMTP is mandatory: the company needs it to send OTP + // emails, and an account without working SMTP could not complete 2FA login. + // With OTP_REQUIRED=false in config.php it is optional — all three fields + // left blank means "no SMTP", and the test send (step 8) and the company_smtp + // row (step 13) are skipped. Partly filled is an error either way. $smtp_host = trim($data['smtp_host'] ?? ''); $smtp_username = trim($data['smtp_username'] ?? ''); $smtp_password = $data['smtp_password'] ?? ''; + $smtp_given = ($smtp_host !== '' || $smtp_username !== '' || $smtp_password !== ''); - if (!$smtp_host || !$smtp_username || !$smtp_password) { - $answer['message'] = 'SMTP configuration is required. Please fill in all SMTP fields.'; + if ((otp_required() || $smtp_given) && (!$smtp_host || !$smtp_username || !$smtp_password)) { + $answer['message'] = otp_required() + ? 'SMTP configuration is required. Please fill in all SMTP fields.' + : 'Fill in SMTP host, username and password, or leave all three blank.'; http_response_code(422); exit(json_encode($answer)); } - // ── Step 6: Normalise SMTP port and encryption ──────────────────────────── - // Clamp to known-good values to prevent storing unsupported configuration. - $smtp_port = trim($data['smtp_port'] ?? '587'); - $smtp_encryption = trim($data['smtp_encryption'] ?? 'tls'); + if ($smtp_given) { + // ── Step 6: Normalise SMTP port and encryption ──────────────────────── + // Clamp to known-good values to prevent storing unsupported configuration. + $smtp_port = trim($data['smtp_port'] ?? '587'); + $smtp_encryption = trim($data['smtp_encryption'] ?? 'tls'); - if (!in_array($smtp_port, ['25', '465', '587'], true)) $smtp_port = '587'; - if (!in_array($smtp_encryption, ['tls', 'ssl', 'none'], true)) $smtp_encryption = 'tls'; + if (!in_array($smtp_port, ['25', '465', '587'], true)) $smtp_port = '587'; + if (!in_array($smtp_encryption, ['tls', 'ssl', 'none'], true)) $smtp_encryption = 'tls'; - // ── Step 7: Encrypt SMTP password ──────────────────────────────────────── - // Uses the same OpenSSL method/iv/key as the rest of the app (from config.php) - // so the stored password can be decrypted by the mailer module. - $encrypted_pass = openssl_encrypt($smtp_password, $method, $pinkey, 0, $iv); + // ── Step 7: Encrypt SMTP password ──────────────────────────────────── + // Uses the same OpenSSL method/iv/key as the rest of the app (from config.php) + // so the stored password can be decrypted by the mailer module. + $encrypted_pass = openssl_encrypt($smtp_password, $method, $pinkey, 0, $iv); - // Assemble a temporary SMTP config for the test send (step 8) - $smtp_config = [ - 'server' => $smtp_host, - 'port' => $smtp_port, - 'username' => $smtp_username, - 'password' => $encrypted_pass, - 'from_name' => $company_name ?: $smtp_username, - 'from_email' => $email ?: $smtp_username, - 'encryption' => $smtp_encryption, - ]; + // Assemble a temporary SMTP config for the test send (step 8) + $smtp_config = [ + 'server' => $smtp_host, + 'port' => $smtp_port, + 'username' => $smtp_username, + 'password' => $encrypted_pass, + 'from_name' => $company_name ?: $smtp_username, + 'from_email' => $email ?: $smtp_username, + 'encryption' => $smtp_encryption, + ]; - // ── Step 8: Silent SMTP test — before any DB writes ────────────────────── - // Sends a test email to the onboarding user's registered address. - // If the mailer throws or exits, no DB records have been created yet, - // so the user can correct their SMTP settings and retry cleanly. - require_once '../../../assets/utils/module/mailer.php'; + // ── Step 8: Silent SMTP test — before any DB writes ────────────────── + // Sends a test email to the onboarding user's registered address. + // If the mailer throws or exits, no DB records have been created yet, + // so the user can correct their SMTP settings and retry cleanly. + require_once '../../../assets/utils/module/mailer.php'; - $mailer = new mailer(['pdo1' => $pdo1]); - $mailer->send_email([ - 'company_id' => 0, - 'smtp' => $smtp_config, - 'to' => $_SESSION['onboarding_email'] ?? $smtp_username, - 'subject' => 'WMS — SMTP Verification', - 'message' => "Your SMTP is working correctly.\n\nSetup is now complete.", - 'channel_name' => $company_name ?: 'WMS', - 'key' => $pinkey, - ]); - // If mailer fails, it calls exit() internally — nothing below this line runs. + $mailer = new mailer(['pdo1' => $pdo1]); + $mailer->send_email([ + 'company_id' => 0, + 'smtp' => $smtp_config, + 'to' => $_SESSION['onboarding_email'] ?? $smtp_username, + 'subject' => 'WMS — SMTP Verification', + 'message' => "Your SMTP is working correctly.\n\nSetup is now complete.", + 'channel_name' => $company_name ?: 'WMS', + 'key' => $pinkey, + ]); + // If mailer fails, it calls exit() internally — nothing below this line runs. + } // ── Step 9: Duplicate channel_name check ───────────────────────────────── // channel_name is the unique identifier used in URLs and API calls — must be globally unique. @@ -226,25 +237,29 @@ try { // ── Step 13: Save company SMTP settings ────────────────────────────────── // Stored with the encrypted password so the mailer module can decrypt and // use it for all outgoing email from this company (OTP, notifications, etc.). - $sth = $pdo1->prepare(" - INSERT INTO company_smtp - (company_id, server, port, username, password, - from_name, from_email, encryption, updated_at) - VALUES - (:company_id, :server, :port, :username, :password, - :from_name, :from_email, :encryption, NOW()) - "); - $sth->execute([ - ':company_id' => $company_id, - ':server' => $smtp_host, - ':port' => $smtp_port, - ':username' => $smtp_username, - ':password' => $encrypted_pass, - ':from_name' => $company_name, - ':from_email' => $email ?: $smtp_username, - ':encryption' => $smtp_encryption, - ]); - db_check($sth, $answer); + // Skipped when no SMTP was given (only allowed with OTP_REQUIRED=false); it + // can be added later under Settings → SMTP. + if ($smtp_given) { + $sth = $pdo1->prepare(" + INSERT INTO company_smtp + (company_id, server, port, username, password, + from_name, from_email, encryption, updated_at) + VALUES + (:company_id, :server, :port, :username, :password, + :from_name, :from_email, :encryption, NOW()) + "); + $sth->execute([ + ':company_id' => $company_id, + ':server' => $smtp_host, + ':port' => $smtp_port, + ':username' => $smtp_username, + ':password' => $encrypted_pass, + ':from_name' => $company_name, + ':from_email' => $email ?: $smtp_username, + ':encryption' => $smtp_encryption, + ]); + db_check($sth, $answer); + } // ── Step 14: Clear onboarding session keys ─────────────────────────────── // These keys are no longer needed and should not persist into the diff --git a/app/login/onboarding.php b/app/login/onboarding.php index d9d62c7..fa437b3 100644 --- a/app/login/onboarding.php +++ b/app/login/onboarding.php @@ -1,6 +1,7 @@
+ SMTP is required to send OTP during login. A verification email will be sent when you finish setup. + + Email OTP is turned off, so SMTP is optional. Leave it blank to skip; + you can add it later under Settings → SMTP. +
@@ -175,11 +185,11 @@