PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.2.9
Yatra – Travel Booking & Tour Operator Software v3.0.2.9
3.0.15 3.0.14 3.0.14.1 3.0.14.2 3.0.12 3.0.13 3.0.11 3.0.10 3.0.9 3.0.8 3.0.7 3.0.6 3.0.5 3.0.5.1 3.0.4 3.0.3 3.0.2.9 3.0.2.7 3.0.2.8 3.0.2.6 trunk 1.0.0 2.0.0 2.0.1 2.0.10 All 83 releases
yatra / app / Controllers / AuthController.php

AuthController.php in Yatra – Travel Booking & Tour Operator Software 3.0.2.9, at app/Controllers/AuthController.php

581 lines 21.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 declare(strict_types=1);
4
5 namespace Yatra\Controllers;
6
7 /**
8 * Authentication Controller
9 * Handles user registration, login, email verification for Yatra customers
10 *
11 * @package Yatra
12 */
13 class AuthController
14 {
15 /**
16 * Register REST API routes
17 */
18 public static function registerRoutes(): void
19 {
20 register_rest_route('yatra/v1', '/auth/login', [
21 'methods' => 'POST',
22 'callback' => [self::class, 'login'],
23 'permission_callback' => '__return_true',
24 ]);
25
26 register_rest_route('yatra/v1', '/auth/register', [
27 'methods' => 'POST',
28 'callback' => [self::class, 'register'],
29 'permission_callback' => '__return_true',
30 ]);
31
32 register_rest_route('yatra/v1', '/auth/resend-verification', [
33 'methods' => 'POST',
34 'callback' => [self::class, 'resendVerification'],
35 'permission_callback' => '__return_true',
36 ]);
37 }
38
39 /**
40 * Register Yatra Customer role
41 */
42 public static function registerCustomerRole(): void
43 {
44 if (!get_role('yatra_customer')) {
45 add_role(
46 'yatra_customer',
47 __('Yatra Customer', 'yatra'),
48 [
49 'read' => true,
50 'edit_posts' => false,
51 'delete_posts' => false,
52 ]
53 );
54 }
55 }
56
57 /**
58 * Block unverified users from WordPress login
59 *
60 * @param \WP_User|\WP_Error|null $user
61 * @param string $username
62 * @param string $password
63 * @return \WP_User|\WP_Error|null
64 */
65 public static function blockUnverifiedUserLogin($user, string $username, string $password)
66 {
67 if (is_wp_error($user)) {
68 return $user;
69 }
70
71 if (!($user instanceof \WP_User)) {
72 return $user;
73 }
74
75 $has_verification_token = get_user_meta($user->ID, 'yatra_verification_token', true);
76 $email_verified = get_user_meta($user->ID, 'yatra_email_verified', true);
77
78 $needs_verification = !empty($has_verification_token);
79
80 if (!$needs_verification) {
81 $meta_exists = metadata_exists('user', $user->ID, 'yatra_email_verified');
82 $needs_verification = $meta_exists && !$email_verified;
83 }
84
85 if ($needs_verification) {
86 $resend_url = add_query_arg([
87 'action' => 'yatra_resend_verification',
88 'email' => urlencode($user->user_email),
89 '_wpnonce' => wp_create_nonce('yatra_resend_verification'),
90 ], wp_login_url());
91
92 $message = sprintf(
93 /* translators: %s: resend verification link */
94 __('<strong>Error:</strong> Please verify your email address before logging in. Check your inbox for the verification link. <br><br><a href="%s">Click here to resend verification email</a>', 'yatra'),
95 esc_url($resend_url)
96 );
97
98 return new \WP_Error('email_not_verified', $message);
99 }
100
101 return $user;
102 }
103
104 /**
105 * Handle resend verification from WordPress login page
106 */
107 public static function handleWpLoginResendVerification(): void
108 {
109 if (!isset($_GET['action']) || $_GET['action'] !== 'yatra_resend_verification') {
110 return;
111 }
112
113 // Verify nonce
114 if (!isset($_GET['_wpnonce']) || !wp_verify_nonce(sanitize_text_field($_GET['_wpnonce']), 'yatra_resend_verification')) {
115 wp_die(__('Your session has expired. Please go back and try again.', 'yatra'));
116 }
117
118 $email = isset($_GET['email']) ? sanitize_email(urldecode($_GET['email'])) : '';
119
120 if (empty($email) || !is_email($email)) {
121 wp_safe_redirect(add_query_arg('login_error', 'invalid_email', wp_login_url()));
122 exit;
123 }
124
125 $user = get_user_by('email', $email);
126
127 if (!$user) {
128 // Redirect with generic success message (don't reveal if email exists)
129 wp_safe_redirect(add_query_arg('checkemail', 'resent', wp_login_url()));
130 exit;
131 }
132
133 // Check if already verified
134 $email_verified = get_user_meta($user->ID, 'yatra_email_verified', true);
135 if ($email_verified === '1') {
136 wp_safe_redirect(add_query_arg('login_error', 'already_verified', wp_login_url()));
137 exit;
138 }
139
140 // Check rate limiting
141 $last_sent = get_user_meta($user->ID, 'yatra_verification_last_sent', true);
142 if ($last_sent && (time() - (int) $last_sent) < 120) {
143 $remaining = 120 - (time() - (int) $last_sent);
144 wp_safe_redirect(add_query_arg([
145 'login_error' => 'rate_limited',
146 'wait' => $remaining,
147 ], wp_login_url()));
148 exit;
149 }
150
151 // Generate new token and send email
152 $verification_token = wp_generate_password(32, false);
153 $secure_token = base64_encode($user->ID . '|' . $verification_token . '|' . time());
154 $secure_token = str_replace(['+', '/', '='], ['-', '_', ''], $secure_token);
155
156 update_user_meta($user->ID, 'yatra_verification_token', $verification_token);
157 update_user_meta($user->ID, 'yatra_verification_token_expiry', time() + (24 * 60 * 60));
158 update_user_meta($user->ID, 'yatra_verification_last_sent', time());
159
160 // Send email
161 $first_name = get_user_meta($user->ID, 'first_name', true) ?: $user->display_name;
162 self::sendVerificationEmail($user->ID, $email, $first_name, $secure_token, true);
163
164 wp_safe_redirect(add_query_arg('checkemail', 'resent', wp_login_url()));
165 exit;
166 }
167
168 /**
169 * Add custom messages to WordPress login page
170 */
171 public static function customLoginMessages(string $message): string
172 {
173 if (isset($_GET['checkemail']) && $_GET['checkemail'] === 'resent') {
174 $message = '<p class="message">' . __('A new verification link has been sent to your email address. Please check your inbox and spam folder.', 'yatra') . '</p>';
175 }
176
177 if (isset($_GET['email_verified']) && $_GET['email_verified'] === '1') {
178 $message = '<p class="message">' . __('Your email has been verified successfully! You can now log in.', 'yatra') . '</p>';
179 }
180
181 return $message;
182 }
183
184 /**
185 * Add custom error messages to WordPress login page
186 */
187 public static function customLoginErrors(\WP_Error $errors): \WP_Error
188 {
189 if (isset($_GET['login_error'])) {
190 switch ($_GET['login_error']) {
191 case 'invalid_email':
192 $errors->add('invalid_email', __('<strong>Error:</strong> Invalid email address.', 'yatra'));
193 break;
194 case 'already_verified':
195 $errors->add('already_verified', __('<strong>Notice:</strong> Your email is already verified. Please log in.', 'yatra'));
196 break;
197 case 'rate_limited':
198 $wait = isset($_GET['wait']) ? (int) $_GET['wait'] : 120;
199 $errors->add('rate_limited', sprintf(
200 /* translators: %d: number of seconds to wait */
201 __('<strong>Error:</strong> Please wait %d seconds before requesting another verification email.', 'yatra'),
202 $wait
203 ));
204 break;
205 }
206 }
207
208 return $errors;
209 }
210
211 /**
212 * Handle login request
213 */
214 public static function login(\WP_REST_Request $request): \WP_REST_Response
215 {
216 $username = sanitize_user($request->get_param('username') ?? '');
217 $password = $request->get_param('password') ?? '';
218 $remember = !empty($request->get_param('remember'));
219
220 if (empty($username) || empty($password)) {
221 return new \WP_REST_Response([
222 'success' => false,
223 'message' => __('Please enter your username/email and password.', 'yatra'),
224 ], 400);
225 }
226
227 // Get user to check verification status
228 $user_check = get_user_by('login', $username);
229 if (!$user_check) {
230 $user_check = get_user_by('email', $username);
231 }
232
233 // Check email verification for Yatra users
234 if ($user_check) {
235 $has_verification_token = get_user_meta($user_check->ID, 'yatra_verification_token', true);
236 $email_verified = get_user_meta($user_check->ID, 'yatra_email_verified', true);
237
238 if (!empty($has_verification_token)) {
239 return new \WP_REST_Response([
240 'success' => false,
241 'message' => __('Please verify your email address before logging in. Check your inbox for the verification link.', 'yatra'),
242 'needs_verification' => true,
243 'email' => $user_check->user_email,
244 ], 403);
245 }
246
247 $meta_exists = metadata_exists('user', $user_check->ID, 'yatra_email_verified');
248 if ($meta_exists && !$email_verified) {
249 return new \WP_REST_Response([
250 'success' => false,
251 'message' => __('Please verify your email address before logging in. Check your inbox for the verification link.', 'yatra'),
252 'needs_verification' => true,
253 'email' => $user_check->user_email,
254 ], 403);
255 }
256 }
257
258 // Authenticate
259 $user = wp_signon([
260 'user_login' => $username,
261 'user_password' => $password,
262 'remember' => $remember,
263 ], is_ssl());
264
265 if (is_wp_error($user)) {
266 $error_message = wp_strip_all_tags($user->get_error_message());
267 if (strpos($error_message, 'incorrect') !== false || strpos($error_message, 'Invalid') !== false) {
268 $error_message = __('Invalid username/email or password. Please try again.', 'yatra');
269 }
270 return new \WP_REST_Response([
271 'success' => false,
272 'message' => $error_message,
273 ], 401);
274 }
275
276 wp_set_current_user($user->ID);
277
278 return new \WP_REST_Response([
279 'success' => true,
280 'message' => __('Login successful! Redirecting...', 'yatra'),
281 'user_id' => $user->ID,
282 ]);
283 }
284
285 /**
286 * Handle registration request
287 */
288 public static function register(\WP_REST_Request $request): \WP_REST_Response
289 {
290 if (!\Yatra\Services\SettingsService::isEnabled('customer_registration')) {
291 return new \WP_REST_Response([
292 'success' => false,
293 'message' => __('New customer registration is disabled.', 'yatra'),
294 ], 403);
295 }
296
297 $first_name = sanitize_text_field($request->get_param('first_name') ?? '');
298 $last_name = sanitize_text_field($request->get_param('last_name') ?? '');
299 $email = sanitize_email($request->get_param('email') ?? '');
300 $phone = sanitize_text_field($request->get_param('phone') ?? '');
301 $password = $request->get_param('password') ?? '';
302 $confirm_password = $request->get_param('confirm_password') ?? '';
303
304 // Validation
305 if (empty($first_name)) {
306 return new \WP_REST_Response([
307 'success' => false,
308 'message' => __('Please enter your first name.', 'yatra'),
309 ], 400);
310 }
311
312 if (empty($last_name)) {
313 return new \WP_REST_Response([
314 'success' => false,
315 'message' => __('Please enter your last name.', 'yatra'),
316 ], 400);
317 }
318
319 if (empty($email) || !is_email($email)) {
320 return new \WP_REST_Response([
321 'success' => false,
322 'message' => __('Please enter a valid email address.', 'yatra'),
323 ], 400);
324 }
325
326 if (empty($password) || strlen($password) < 8) {
327 return new \WP_REST_Response([
328 'success' => false,
329 'message' => __('Password must be at least 8 characters long.', 'yatra'),
330 ], 400);
331 }
332
333 if ($password !== $confirm_password) {
334 return new \WP_REST_Response([
335 'success' => false,
336 'message' => __('Passwords do not match.', 'yatra'),
337 ], 400);
338 }
339
340 if (email_exists($email)) {
341 return new \WP_REST_Response([
342 'success' => false,
343 'message' => __('An account with this email already exists. Please login instead.', 'yatra'),
344 ], 409);
345 }
346
347 // Generate username
348 $username = sanitize_user(current(explode('@', $email)));
349 $original_username = $username;
350 $counter = 1;
351
352 while (username_exists($username)) {
353 $username = $original_username . $counter;
354 $counter++;
355 }
356
357 // Create user
358 $user_id = wp_create_user($username, $password, $email);
359
360 if (is_wp_error($user_id)) {
361 $error_message = wp_strip_all_tags($user_id->get_error_message());
362 return new \WP_REST_Response([
363 'success' => false,
364 'message' => $error_message,
365 ], 500);
366 }
367
368 // Assign Yatra Customer role
369 $user = new \WP_User($user_id);
370 $user->set_role('yatra_customer');
371
372 // Update user meta
373 wp_update_user([
374 'ID' => $user_id,
375 'first_name' => $first_name,
376 'last_name' => $last_name,
377 'display_name' => $first_name . ' ' . $last_name,
378 ]);
379
380 if (!empty($phone)) {
381 update_user_meta($user_id, 'billing_phone', $phone);
382 update_user_meta($user_id, 'phone', $phone);
383 }
384
385 // Email verification
386 update_user_meta($user_id, 'yatra_email_verified', '0');
387
388 $verification_token = wp_generate_password(32, false);
389 $secure_token = base64_encode($user_id . '|' . $verification_token . '|' . time());
390 $secure_token = str_replace(['+', '/', '='], ['-', '_', ''], $secure_token);
391
392 update_user_meta($user_id, 'yatra_verification_token', $verification_token);
393 update_user_meta($user_id, 'yatra_verification_token_expiry', time() + (24 * 60 * 60));
394
395 // Send verification email
396 self::sendVerificationEmail($user_id, $email, $first_name, $secure_token);
397
398 // Admin notification
399 wp_new_user_notification($user_id, null, 'admin');
400
401 return new \WP_REST_Response([
402 'success' => true,
403 'message' => __('Registration successful! Please check your email to verify your account before logging in.', 'yatra'),
404 'user_id' => $user_id,
405 'require_verification' => true,
406 ]);
407 }
408
409 /**
410 * Handle resend verification request
411 */
412 public static function resendVerification(\WP_REST_Request $request): \WP_REST_Response
413 {
414 $email = sanitize_email($request->get_param('email') ?? '');
415
416 if (empty($email) || !is_email($email)) {
417 return new \WP_REST_Response([
418 'success' => false,
419 'message' => __('Please enter a valid email address.', 'yatra'),
420 ], 400);
421 }
422
423 $user = get_user_by('email', $email);
424
425 if (!$user) {
426 return new \WP_REST_Response([
427 'success' => true,
428 'message' => __('If an account with this email exists and is pending verification, a new verification link has been sent.', 'yatra'),
429 ]);
430 }
431
432 $email_verified = get_user_meta($user->ID, 'yatra_email_verified', true);
433
434 if ($email_verified === '1' || $email_verified === 1 || $email_verified === true) {
435 return new \WP_REST_Response([
436 'success' => false,
437 'message' => __('This email is already verified. You can login now.', 'yatra'),
438 ], 400);
439 }
440
441 $meta_exists = metadata_exists('user', $user->ID, 'yatra_email_verified');
442 $has_verification_token = get_user_meta($user->ID, 'yatra_verification_token', true);
443
444 if (!$meta_exists && empty($has_verification_token)) {
445 return new \WP_REST_Response([
446 'success' => true,
447 'message' => __('If an account with this email exists and is pending verification, a new verification link has been sent.', 'yatra'),
448 ]);
449 }
450
451 // Rate limiting
452 $last_sent = get_user_meta($user->ID, 'yatra_verification_last_sent', true);
453 if ($last_sent && (time() - (int) $last_sent) < 120) {
454 $remaining = 120 - (time() - (int) $last_sent);
455 return new \WP_REST_Response([
456 'success' => false,
457 'message' => __('Please wait before requesting another verification email.', 'yatra'),
458 'rate_limited' => true,
459 'remaining_seconds' => $remaining,
460 ], 429);
461 }
462
463 // Generate new token
464 $verification_token = wp_generate_password(32, false);
465 $secure_token = base64_encode($user->ID . '|' . $verification_token . '|' . time());
466 $secure_token = str_replace(['+', '/', '='], ['-', '_', ''], $secure_token);
467
468 update_user_meta($user->ID, 'yatra_verification_token', $verification_token);
469 update_user_meta($user->ID, 'yatra_verification_token_expiry', time() + (24 * 60 * 60));
470 update_user_meta($user->ID, 'yatra_verification_last_sent', time());
471
472 // Send email
473 $first_name = get_user_meta($user->ID, 'first_name', true) ?: $user->display_name;
474 self::sendVerificationEmail($user->ID, $email, $first_name, $secure_token, true);
475
476 return new \WP_REST_Response([
477 'success' => true,
478 'message' => __('A new verification link has been sent to your email address. Please check your inbox.', 'yatra'),
479 ]);
480 }
481
482 /**
483 * Handle email verification
484 */
485 public static function handleEmailVerification(): void
486 {
487 $secure_token = get_query_var('yatra_verify_email');
488
489 if (empty($secure_token)) {
490 return;
491 }
492
493 // Decode token
494 $secure_token = str_replace(['-', '_'], ['+', '/'], $secure_token);
495 $decoded = base64_decode($secure_token);
496
497 if (!$decoded || strpos($decoded, '|') === false) {
498 self::showVerificationError(__('Invalid verification link.', 'yatra'));
499 return;
500 }
501
502 $parts = explode('|', $decoded);
503 if (count($parts) < 2) {
504 self::showVerificationError(__('Invalid verification link.', 'yatra'));
505 return;
506 }
507
508 $user_id = (int) $parts[0];
509 $token = $parts[1];
510
511 if ($user_id <= 0 || empty($token)) {
512 self::showVerificationError(__('Invalid verification link.', 'yatra'));
513 return;
514 }
515
516 $stored_token = get_user_meta($user_id, 'yatra_verification_token', true);
517 $token_expiry = get_user_meta($user_id, 'yatra_verification_token_expiry', true);
518
519 if (empty($stored_token) || $stored_token !== $token) {
520 self::showVerificationError(__('Invalid or expired verification link.', 'yatra'));
521 return;
522 }
523
524 if ($token_expiry && time() > (int) $token_expiry) {
525 self::showVerificationError(__('This verification link has expired. Please register again.', 'yatra'));
526 return;
527 }
528
529 // Mark as verified
530 update_user_meta($user_id, 'yatra_email_verified', '1');
531 delete_user_meta($user_id, 'yatra_verification_token');
532 delete_user_meta($user_id, 'yatra_verification_token_expiry');
533
534 $redirect_url = add_query_arg(['email_verified' => '1'], yatra_get_checkout_url());
535 wp_safe_redirect($redirect_url);
536 exit;
537 }
538
539 /**
540 * Send verification email
541 */
542 private static function sendVerificationEmail(int $user_id, string $email, string $first_name, string $secure_token, bool $isResend = false): void
543 {
544 $verification_url = home_url('/yatra-verify-email/' . $secure_token . '/');
545 $site_name = get_bloginfo('name');
546 $subject = sprintf(__('[%s] Please verify your email address', 'yatra'), $site_name);
547
548 $intro = $isResend
549 ? __("You requested a new verification link for your account at %s.", 'yatra')
550 : __("Thank you for registering at %s.", 'yatra');
551
552 $message = sprintf(
553 __("Hello %s,\n\n" . $intro . "\n\nPlease click the link below to verify your email address:\n\n%s\n\nThis link will expire in 24 hours.\n\nIf you did not " . ($isResend ? "request this" : "create this account") . ", please ignore this email.\n\nBest regards,\n%s", 'yatra'),
554 $first_name,
555 $site_name,
556 $verification_url,
557 $site_name
558 );
559
560 $headers = ['Content-Type: text/plain; charset=UTF-8'];
561 wp_mail($email, $subject, $message, $headers);
562 }
563
564 /**
565 * Show verification error page
566 */
567 private static function showVerificationError(string $message): void
568 {
569 wp_die(
570 '<div style="text-align: center; padding: 50px; font-family: -apple-system, BlinkMacSystemFont, \'Segoe UI\', Roboto, sans-serif;">
571 <h1 style="color: #dc2626; margin-bottom: 20px;">Verification Failed</h1>
572 <p style="color: #4b5563; font-size: 16px; margin-bottom: 30px;">' . esc_html($message) . '</p>
573 <a href="' . esc_url(home_url()) . '" style="display: inline-block; background: #3b82f6; color: #fff; padding: 12px 24px; border-radius: 8px; text-decoration: none; font-weight: 600;">Go to Homepage</a>
574 </div>',
575 __('Verification Failed', 'yatra'),
576 ['response' => 400]
577 );
578 }
579 }
580
581