From a6413da67a4579866f0fd0323ddb23a7b6b9654a Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Fri, 18 Sep 2026 17:00:53 +0000
Subject: [PATCH] =Cleaning up the Integrations setup a bit, started the Helcim integration work too
---
inc/rest/routes/LoginRoutes.php | 232 +++++++++++++++++++++------------------------------------
1 files changed, 86 insertions(+), 146 deletions(-)
diff --git a/inc/rest/routes/LoginRoutes.php b/inc/rest/routes/LoginRoutes.php
index 0ef1550..9ff7f51 100644
--- a/inc/rest/routes/LoginRoutes.php
+++ b/inc/rest/routes/LoginRoutes.php
@@ -1,6 +1,11 @@
<?php
namespace JVBase\rest\routes;
+use Exception;
+use JVBase\Instance;
+use JVBase\managers\Emails;
+use JVBase\managers\MagicLinks;
+use JVBase\managers\Referrals;
use JVBase\registrar\Registrar;
use JVBase\rest\Rest;
use JVBase\rest\Route;
@@ -21,6 +26,7 @@
*/
class LoginRoutes extends Rest
{
+ use Instance;
protected ?string $requestId = null;
protected bool $hasMagicLink = false;
@@ -30,8 +36,8 @@
$this->cacheTtl = WEEK_IN_SECONDS;
parent::__construct();
+ $this->hasMagicLink = Site::has('magic_link');
- $this->hasMagicLink = Site::has('magicLink');
}
public function registerRoutes(): void
@@ -94,7 +100,8 @@
->register();
// Magic link endpoint
- if ($this->hasMagicLink) {
+
+ if (Site::has('magic_link')) {
Route::for('auth/magic')
->post([$this, 'handleMagicLink'])
->args([
@@ -135,29 +142,27 @@
*/
public function handleLogin(WP_REST_Request $request): WP_REST_Response
{
+ error_log('Handling login...');
$email = sanitize_email($request->get_param('user_email'));
$password = $request->get_param('user_password');
$remember = (bool) $request->get_param('remember_me');
$redirect_to = $request->get_param('redirect_to');
// Verify Turnstile
+
if (!$this->verifyTurnstile($request->get_param('cf-turnstile-response') ?? '')) {
+ error_log('[handleLogin]Turnstile failed');
return $this->error(
'Security verification failed. Please try again.',
'turnstile_failed',
403
);
+ } else {
+ error_log('[handleLogin]Turnstile succeeded');
}
- // Attempt authentication
- $user = wp_authenticate($email, $password);
-
+ $user = $this->login($email, $password, $remember);
if (is_wp_error($user)) {
- $this->auditLog('login_failed', [
- 'email' => $email,
- 'error' => $user->get_error_code(),
- ]);
-
return $this->error(
$this->getLoginErrorMessage($user),
$user->get_error_code(),
@@ -165,21 +170,6 @@
);
}
- // Set auth cookie
- wp_clear_auth_cookie();
- wp_set_current_user($user->ID);
- wp_set_auth_cookie($user->ID, $remember);
-
- // Store session fingerprint
- $this->storeSessionFingerprint($user->ID, $request);
-
- do_action('wp_login', $user->user_login, $user);
-
- $this->auditLog('login_success', [
- 'user_id' => $user->ID,
- 'email' => $email,
- ]);
-
// Get redirect URL
$redirect = $this->getRedirectUrl($user, $redirect_to);
@@ -191,6 +181,40 @@
]);
}
+ public function login(string $email, string $password, bool $remember, ?WP_REST_Request $request = null):WP_User|WP_Error
+ {
+ // Attempt authentication
+ $user = wp_authenticate($email, $password);
+
+ if (is_wp_error($user)) {
+ $this->auditLog('login_failed', [
+ 'email' => $email,
+ 'error' => $user->get_error_code(),
+ ]);
+
+ return $user;
+ }
+
+ // Set auth cookie
+ wp_clear_auth_cookie();
+ wp_set_current_user($user->ID);
+ wp_set_auth_cookie($user->ID, $remember);
+
+ // Store session fingerprint
+ if ($request) {
+ $this->storeSessionFingerprint($user->ID, $request);
+ }
+
+
+ do_action('wp_login', $user->user_login, $user);
+
+ $this->auditLog('login_success', [
+ 'user_id' => $user->ID,
+ 'email' => $email,
+ ]);
+ return $user;
+ }
+
/**
* Handle user registration
*/
@@ -239,7 +263,7 @@
);
if (is_wp_error($user_id)) {
- $this->logError('Registration failed', [
+ $this->logError('Registration failed', 'handleRegister', [
'email' => $email,
'error' => $user_id->get_error_message(),
]);
@@ -273,7 +297,7 @@
// Send magic link for email verification
if ($this->hasMagicLink) {
- JVB()->magicLink()->sendMagicLink($email, 'signup', [
+ MagicLinks::getInstance()->sendMagicLink($email, 'signup', [
'name' => $name,
'role' => $role,
]);
@@ -324,10 +348,10 @@
// Use magic link if available, otherwise standard WP reset
if ($this->hasMagicLink) {
- $result = JVB()->magicLink()->sendMagicLink($email, 'reset');
+ $result = MagicLinks::getInstance()->sendMagicLink($email, 'reset');
if (is_wp_error($result)) {
- $this->logError('Magic link send failed', [
+ $this->logError('Magic link send failed', 'handleLostPassword',[
'email' => $email,
'error' => $result->get_error_message(),
]);
@@ -336,12 +360,12 @@
// Standard WordPress password reset
$key = get_password_reset_key($user);
if (is_wp_error($key)) {
- $this->logError('Reset key generation failed', [
+ $this->logError('Reset key generation failed', 'handleLostPassword', [
'email' => $email,
'error' => $key->get_error_message(),
]);
} else {
- $success = JVB()->email()->sendPasswordResetEmail($user, $key);
+ $success = Emails::getInstance()->sendPasswordResetEmail($user, $key);
}
}
@@ -392,8 +416,7 @@
if (is_wp_error($user)) {
return $this->error(
'Invalid or expired reset link.',
- 'invalid_key',
- 400
+ 'invalid_key'
);
}
@@ -415,11 +438,11 @@
*/
public function handleMagicLink(WP_REST_Request $request): WP_REST_Response
{
- if (!$this->hasMagicLink) {
+
+ if (!Site::has('magic_link')) {
return $this->error(
'Magic link authentication is not enabled.',
- 'feature_disabled',
- 400
+ 'feature_disabled'
);
}
@@ -442,10 +465,10 @@
}
// Send magic link
- $result = JVB()->magicLink()->sendMagicLink($email, $type, $context);
+ $result = MagicLinks::getInstance()->sendMagicLink($email, $type, $context);
if (is_wp_error($result)) {
- $this->logError('Magic link send failed', [
+ $this->logError('Magic link send failed', 'handleMagicLink', [
'email' => $email,
'type' => $type,
'error' => $result->get_error_message(),
@@ -453,8 +476,7 @@
return $this->error(
$result->get_error_message(),
- $result->get_error_code(),
- 400
+ $result->get_error_code()
);
}
@@ -495,87 +517,6 @@
]);
}
- /************************************************************
- * SESSION FINGERPRINTING
- *
- * Detects session hijacking by validating that session hasn't
- * moved to a different device/network. Uses IP class (not full IP)
- * to allow mobile network changes without breaking sessions.
- ************************************************************/
-
- /**
- * Store session fingerprint for hijacking detection
- */
- protected function storeSessionFingerprint(int $user_id, WP_REST_Request $request): void
- {
- if (!defined('JVB_SESSION_FINGERPRINT') || !JVB_SESSION_FINGERPRINT) {
- return;
- }
-
- $fingerprint = $this->generateSessionFingerprint($request);
- update_user_meta($user_id, BASE . 'session_fingerprint', $fingerprint);
- update_user_meta($user_id, BASE . 'session_timestamp', time());
- }
-
- /**
- * Generate session fingerprint for hijacking detection
- */
- protected function generateSessionFingerprint(WP_REST_Request $request): string
- {
- return hash('sha256', implode('|', [
- $request->get_header('User-Agent') ?? '',
- // Use IP class instead of full IP to allow for mobile network changes
- $this->getIPClass(
- $request->get_header('X-Forwarded-For')
- ?: $request->get_header('X-Real-IP')
- ?: $_SERVER['REMOTE_ADDR'] ?? ''
- )
- ]));
- }
-
- /**
- * Get IP class (first 3 octets) for session validation
- * Allows for minor IP changes (common with mobile networks)
- */
- protected function getIPClass(string $ip): string
- {
- $parts = explode('.', $ip);
- return implode('.', array_slice($parts, 0, 3));
- }
-
- /**
- * Validate session fingerprint against stored value
- */
- protected function validateSessionFingerprint(int $user_id, WP_REST_Request $request): bool
- {
- // Only enforce if enabled in config
- if (!defined('JVB_SESSION_FINGERPRINT') || !JVB_SESSION_FINGERPRINT) {
- return true;
- }
-
- $stored = get_user_meta($user_id, BASE . 'session_fingerprint', true);
- $current = $this->generateSessionFingerprint($request);
-
- if (empty($stored)) {
- // First request - store fingerprint
- update_user_meta($user_id, BASE . 'session_fingerprint', $current);
- update_user_meta($user_id, BASE . 'session_timestamp', time());
- return true;
- }
-
- // Compare using timing-safe comparison
- return hash_equals($stored, $current);
- }
-
- /**
- * Clear session fingerprint (call on logout)
- */
- protected function clearSessionFingerprint(int $user_id): void
- {
- delete_user_meta($user_id, BASE . 'session_fingerprint');
- delete_user_meta($user_id, BASE . 'session_timestamp');
- }
-
/**************************************************************
* HELPERS
**************************************************************/
@@ -654,13 +595,13 @@
}
try {
- JVB()->referrals()->processReferralCode($user_id, $referral_code);
- } catch (\Exception $e) {
- $this->logError('Referral processing failed', [
+ Referrals::getInstance()->processReferralCode($user_id, $referral_code);
+ } catch (Exception $e) {
+ $this->logError('Referral processing failed', 'processReferralCode', [
'user_id' => $user_id,
'code' => $referral_code,
'error' => $e->getMessage(),
- ], 'warning');
+ ]);
}
}
@@ -697,37 +638,36 @@
*/
protected function sanitizeFieldValue(mixed $value, string $type):string|int
{
- switch ($type) {
- case 'email':
- return sanitize_email($value);
- case 'url':
- return esc_url_raw($value);
- case 'textarea':
- return sanitize_textarea_field($value);
- case 'number':
- return absint($value);
- default:
- return sanitize_text_field($value);
- }
+ return match ($type) {
+ 'email' => sanitize_email($value),
+ 'url' => esc_url_raw($value),
+ 'textarea' => sanitize_textarea_field($value),
+ 'number' => absint($value),
+ default => sanitize_text_field($value),
+ };
+ }
+
+ public static function auth():array
+ {
+ return (new self)->buildAuth();
}
protected function buildAuth(?int $user = null): array
{
- if (is_user_logged_in()) {
- $user = ($user) ?: get_current_user_id();
+ $userId = $user ?? (is_user_logged_in() ? get_current_user_id() : 0);
+
+ if ($userId) {
return [
'authenticated' => true,
- 'user' => $user,
- 'nonces' => $this->getUserNonces($user)
+ 'user' => $userId,
+ 'nonces' => $this->getUserNonces($userId),
];
}
return [
'authenticated' => false,
- 'user' => false,
- 'nonces' => [
- 'wp_rest' => wp_create_nonce('wp_rest')
- ]
+ 'user' => false,
+ 'nonces' => ['wp_rest' => wp_create_nonce('wp_rest')],
];
}
protected function getUserNonces(int $userID):array {
@@ -740,7 +680,7 @@
if (Site::has('favourites')) {
$nonces['favourites'] = wp_create_nonce('favourites-'.$userID);
}
- if (!empty(Registrar::getFeatured('karma'))) {
+ if (!empty(Registrar::withFeature('karma'))) {
$nonces['votes'] = wp_create_nonce('votes-'.$userID);
}
if (Site::has('notifications')) {
--
Gitblit v1.10.0