From bad59c66549eb601fed963ed013f9b79305ca003 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Wed, 07 Jan 2026 20:09:14 +0000
Subject: [PATCH] =Feedblock integrated with refactored taxonomy selector
---
inc/rest/routes/LoginRoutes.php | 261 +++++++++++++++++++++++++++++++++++----------------
1 files changed, 178 insertions(+), 83 deletions(-)
diff --git a/inc/rest/routes/LoginRoutes.php b/inc/rest/routes/LoginRoutes.php
index ec82f0d..226692b 100644
--- a/inc/rest/routes/LoginRoutes.php
+++ b/inc/rest/routes/LoginRoutes.php
@@ -1,14 +1,12 @@
<?php
namespace JVBase\rest\routes;
-use JVBase\managers\EmailManager;
-use JVBase\managers\LoginManager;
-use JVBase\managers\MagicLinkManager;
use JVBase\rest\RestRouteManager;
use JVBase\utility\Features;
use WP_REST_Request;
use WP_REST_Response;
use WP_Error;
+use WP_Session_Tokens;
use WP_User;
if (!defined('ABSPATH')) {
@@ -17,19 +15,12 @@
class LoginRoutes extends RestRouteManager
{
- protected EmailManager $emailManager;
- protected MagicLinkManager $magic_link;
- protected LoginManager $loginManager;
protected ?string $requestId = null;
public function __construct()
{
$this->cache_name = 'auth';
$this->cache_ttl = WEEK_IN_SECONDS;
- $this->emailManager = new EmailManager();
- if (Features::forSite()->has('magicLink')) {
- $this->magic_link = new MagicLinkManager();
- }
parent::__construct();
}
@@ -113,7 +104,7 @@
]);
register_rest_route($this->namespace, '/auth/register', [
- 'method' => 'POST',
+ 'methods' => 'POST',
'callback' => [$this, 'handleRegister'],
'permission_callback' => [$this, 'checkRateLimit'],
]);
@@ -134,14 +125,15 @@
public function handleLogin(WP_REST_Request $request): WP_REST_Response
{
+ $data = $request->get_json_params();
// Verify Turnstile
- if (!$this->verifyTurnstile($request->get_param('cf-turnstile-response') ?? '')) {
+ if (!$this->verifyTurnstile($data['cf-turnstile-response'] ?? '')) {
return $this->error('Security verification failed', 'turnstile_failed', 403);
}
- $username = $request->get_param('user_email');
- $password = $request->get_param('user_password');
- $remember = (bool)$request->get_param('remember_me');
+ $username = sanitize_email($data['user_email'] ?? '');
+ $password = $data['user_password'] ?? '';
+ $remember = (bool)($data['remember_me'] ?? false);
// Check for account lockout
$lockout = $this->checkAccountLockout($username);
@@ -152,35 +144,42 @@
429
);
}
+ return $this->login($username, $password, $remember, $request);
+ }
+ public function login(string $username, string $password, bool $remember, ?WP_REST_Request $request = null):WP_REST_Response|bool
+ {
// Attempt login
$user = wp_signon([
'user_login' => $username,
- 'user_email' => $username,
'user_password' => $password,
'remember' => $remember
- ], false);
+ ], is_ssl());
+
if (is_wp_error($user)) {
// Track failed attempt
$this->trackFailedLogin($username);
- return $this->error(
+ return ($request) ? $this->error(
'Invalid username or password',
'login_failed',
401
- );
+ ) : false;
}
-
// Clear failed attempts on success
$this->clearFailedAttempts($username);
// Set auth cookie with remember me flag
wp_set_current_user($user->ID);
- wp_set_auth_cookie($user->ID, $remember);
+ wp_set_auth_cookie($user->ID, $remember, is_ssl());
+
+
// Store session fingerprint for hijacking protection
- $this->storeSessionFingerprint($user->ID, $request);
+ if ($request) {
+ $this->storeSessionFingerprint($user->ID, $request);
+ }
// Trigger WordPress login action
do_action('wp_login', $user->user_login, $user);
@@ -191,11 +190,33 @@
'remember' => $remember
]);
- return $this->success([
+ return ($request) ? $this->success([
'message' => 'Login successful',
'user' => $this->formatUserData($user),
- 'redirect' => $this->getLoginRedirect($user)
- ]);
+ 'redirect' => $this->getRedirect($user, $request->get_param('redirect_to')),
+ 'auth' => $this->buildAuth($user->ID)
+ ]) : true;
+ }
+
+ protected function getUserNonces(int $userID):array {
+ $nonces = [
+ 'wp_rest' => wp_create_nonce('wp_rest'),
+ ];
+ if (Features::forSite()->has('dashboard')) {
+ $nonces['dash'] = wp_create_nonce('dash-'.$userID);
+ }
+ if (Features::forSite()->has('favourites')) {
+ $nonces['favourites'] = wp_create_nonce('favourites-'.$userID);
+ }
+ if (Features::anyContentHas('karma') ||
+ Features::anyTaxonomyHas('karma') ||
+ Features::anyUserHas('karma')) {
+ $nonces['votes'] = wp_create_nonce('votes-'.$userID);
+ }
+ if (Features::forSite()->has('notifications')) {
+ $nonces['notifications'] = wp_create_nonce('notifications-'.$userID);
+ }
+ return $nonces;
}
/**
@@ -215,27 +236,64 @@
wp_logout();
return $this->success([
- 'message' => 'Logged out successfully'
+ 'message' => 'Logged out successfully',
+ 'redirect' => $this->getRedirect(get_userdata($user_id), $request->get_param('redirect_to'), 'logout')
]);
}
+ protected function buildAuth(?int $user = null): array
+ {
+ if (is_user_logged_in()) {
+ $user = ($user) ?: get_current_user_id();
+ return [
+ 'authenticated' => true,
+ 'user' => $user,
+ 'nonces' => $this->getUserNonces($user),
+ 'session_id' => $this->getSessionId($user)
+ ];
+ }
+
+ return [
+ 'authenticated' => false,
+ 'currentUser' => false,
+ 'nonces' => [
+ 'wp_rest' => wp_create_nonce('wp_rest')
+ ],
+ 'session_id' => null
+ ];
+ }
+
+ /**
+ * Get unique session identifier that changes on login/logout
+ */
+ protected function getSessionId(int $user_id): string
+ {
+ $token = wp_get_session_token(); // Current session token
+
+ if (!$token) {
+ // Fallback to a hash based on user ID and current timestamp
+ // This will be replaced once the session token is available
+ return md5($user_id . time());
+ }
+
+ return md5($token);
+ }
+
/**
* Get current authentication status
*/
public function getAuthStatus(WP_REST_Request $request): WP_REST_Response
{
- if (!is_user_logged_in()) {
- return $this->success([
- 'authenticated' => false
- ]);
- }
- $user = wp_get_current_user();
+ $responseData = $this->buildAuth();
- return $this->success([
- 'authenticated' => true,
- 'user' => $this->formatUserData($user)
- ]);
+ $response = $this->success($responseData);
+
+ // Add caching headers
+ $response->header('Cache-Control', 'private, max-age=300'); // 5 minutes
+ $response->header('Vary', 'Cookie'); // Important for nginx
+
+ return $response;
}
/**
@@ -312,6 +370,9 @@
wp_set_current_user($user->ID);
wp_set_auth_cookie($user->ID, true);
+ if (session_status() === PHP_SESSION_ACTIVE) {
+ session_regenerate_id(true);
+ }
// Store session fingerprint
$this->storeSessionFingerprint($user->ID, $request);
@@ -364,6 +425,7 @@
$name = sanitize_text_field($data['name'] ?? '');
$email = sanitize_email($data['email'] ?? '');
+ $referral_code = $request->get_param('referral_code')??'';
$user_type = sanitize_text_field($data['user_select'] ?? 'subscriber');
// Validate fields
@@ -375,14 +437,6 @@
return $this->error('Email is required', 'missing_email', 400, 'email');
}
- // Spam prevention
- if ($user_type === 'subscriber' && count(JVB_USER) > 0) {
- $registerable = array_filter(JVB_USER, fn($config) => $config['can_register'] ?? false);
- if (!empty($registerable)) {
- return $this->error('Please select a valid account type', 'invalid_user_type', 400, 'user_select');
- }
- }
-
// Check if role can register
if ($user_type !== 'subscriber') {
if (!isset(JVB_USER[$user_type]) || empty(JVB_USER[$user_type]['can_register'])) {
@@ -395,6 +449,19 @@
return $this->error('Email already registered', 'duplicate_email', 400, 'email');
}
+ // Validate referral code if provided
+ $referrer_id = null;
+ if ($referral_code) {
+ $code = strtoupper(sanitize_text_field($referral_code));
+ $referrer = JVB()->referrals()->getUserByReferralCode($code);
+
+ if (!$referrer) {
+ return $this->error('Invalid referral code', 'invalid_code', 400);
+ }
+
+ $referrer_id = $referrer->ID;
+ }
+
// Allow WP plugins to add registration errors
$errors = new WP_Error();
$errors = apply_filters('registration_errors', $errors, $email, $email);
@@ -407,27 +474,42 @@
);
}
- // Create user
- $user_id = wp_create_user($email, wp_generate_password(), $email);
+ // Update user data
+ $role = ($referrer_id) ? get_option(BASE . 'referral_role', BASE . 'client') : jvbCheckBase($user_type);
+ $userData = [
+ 'user_login' => $email,
+ 'user_email' => $email,
+ 'display_name' => $name,
+ 'first_name' => strtok($name, ' '),
+ 'role' => $role
+ ];
- if (is_wp_error($user_id)) {
- return $this->error($user_id->get_error_message(), 'user_creation_failed', 500);
+ // Add password if provided, otherwise generate one
+ $password = $request->get_param('password');
+ if ($password) {
+ $userData['user_pass'] = $password;
+ } else {
+ $userData['user_pass'] = wp_generate_password(20, true, true);
}
- // Update user data
- wp_update_user([
- 'ID' => $user_id,
- 'display_name' => $name,
- 'first_name' => strtok($name,' ')
- ]);
+ $user_id = wp_insert_user($userData);
+
+ if (is_wp_error($user_id)) {
+ return $this->error(
+ $user_id->get_error_message(),
+ 'registration_failed',
+ 500
+ );
+ }
+
+ // Process referral if code was provided
+ if ($referrer_id) {
+ update_user_meta($user_id, BASE . 'pending_referral_code', $referral_code);
+ }
// Set role
- $user = new WP_User($user_id);
- if ($user_type === 'subscriber') {
- $user->set_role('subscriber');
- } else {
- $role = JVB_USER[$user_type]['role'] ?? 'subscriber';
- $user->set_role($role);
+ $user = get_userdata($user_id);
+ if ($user_type !== 'subscriber') {
// Check if needs approval
if (Features::forMembership()->has('memberVerified') &&
@@ -445,9 +527,6 @@
wp_mkdir_p($target_dir);
}
- // Save additional fields
- update_user_meta($user_id, BASE . 'user_type', $user_type);
-
// Process additional fields from form
foreach ($data as $key => $value) {
if (in_array($key, ['name', 'email', 'action', 'request_id', 'user_select', 'cf-turnstile-response'])) {
@@ -456,13 +535,31 @@
update_user_meta($user_id, BASE . $key, sanitize_text_field($value));
}
+ $redirect = $this->getRedirect($user, $request->get_param('redirect_to')??get_home_url(null,'/dash'), 'register');
+
// Handle token handlers
do_action('jvbUserRegistered', $user_id, $email, $data);
+ $magic_link_result = JVB()->magicLink()?->sendMagicLink(
+ $email,
+ 'login',
+ [
+ 'user_id' => $user_id,
+ 'redirect' => $redirect
+ ]
+ );
+ if (is_wp_error($magic_link_result)) {
+ return $this->error(
+ 'Account created but failed to send verification email. Please use password reset.',
+ 'magic_link_failed',
+ 500
+ );
+ }
return $this->success([
'message' => 'Registration successful! Check your email.',
- 'user_id' => $user_id
+ 'user_id' => $user_id,
+ 'redirect' => $redirect
]);
}
/**************************************************************
@@ -487,18 +584,28 @@
];
}
- /**
- * Get login redirect URL based on user role
- */
- protected function getLoginRedirect(WP_User $user): string
+ protected function getRedirect(WP_User $user, string $url, string $context = 'login'):string
{
+ if (!empty($url)) {
+ $url = sanitize_url($url);
+ if (wp_validate_redirect($url)) {
+ return $url;
+ }
+ }
+
+ // Redirect to custom dashboard for members
+ if (function_exists('isOurPeople') && isOurPeople()) {
+ return home_url('/dash');
+ }
+
+ // Admins can go to wp-admin if they want (but only if not using custom dashboard)
if (user_can($user, 'manage_options')) {
return admin_url();
}
- // Redirect to dashboard for members
- if (function_exists('isOurPeople') && isOurPeople()) {
- return home_url('/dash');
+ $custom_redirect = get_option(BASE . 'after_'.$context.'_redirect');
+ if ($custom_redirect) {
+ return $custom_redirect;
}
return home_url();
@@ -638,18 +745,6 @@
return true;
}
- protected function verifyTurnstile(string $token): bool
- {
- if (!Features::hasIntegration('cloudflare') || !JVB()->connect('cloudflare')->isSetUp()) {
- return true;
- }
-
- if (empty($token)) {
- return false;
- }
-
- return JVB()->connect('cloudflare')->verifyTurnstile($token);
- }
/**
* Helper to return error response
--
Gitblit v1.10.0