diff --git a/admin-spa/src/App.tsx b/admin-spa/src/App.tsx index 1226bb1..0041cfd 100644 --- a/admin-spa/src/App.tsx +++ b/admin-spa/src/App.tsx @@ -1,5 +1,6 @@ import React, { useEffect, useState } from 'react'; -import { HashRouter, Routes, Route, NavLink, useLocation, useParams, Navigate } from 'react-router-dom'; +import { HashRouter, Routes, Route, NavLink, useLocation, useParams, Navigate, Link } from 'react-router-dom'; +import { Login } from './routes/Login'; import Dashboard from '@/routes/Dashboard'; import DashboardRevenue from '@/routes/Dashboard/Revenue'; import DashboardOrders from '@/routes/Dashboard/Orders'; @@ -19,7 +20,6 @@ import ProductAttributes from '@/routes/Products/Attributes'; import CouponsIndex from '@/routes/Coupons'; import CouponNew from '@/routes/Coupons/New'; import CustomersIndex from '@/routes/Customers'; -import { Login } from '@/routes/Login'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { LayoutDashboard, ReceiptText, Package, Tag, Users, Settings as SettingsIcon, Maximize2, Minimize2, Loader2 } from 'lucide-react'; import { Toaster } from 'sonner'; @@ -441,7 +441,7 @@ function AuthWrapper() { }); // In standalone mode, trust the initial PHP auth check - // No need for additional API call since PHP already verified the session + // PHP uses wp_signon which sets proper WordPress cookies if (window.WNW_CONFIG?.standaloneMode) { setIsAuthenticated(window.WNW_CONFIG.isAuthenticated); setIsChecking(false); @@ -464,7 +464,7 @@ function AuthWrapper() { } if (location.pathname === '/login' && isAuthenticated) { - return ; + return ; } return ( diff --git a/admin-spa/src/lib/api.ts b/admin-spa/src/lib/api.ts index 41cc36b..833ef90 100644 --- a/admin-spa/src/lib/api.ts +++ b/admin-spa/src/lib/api.ts @@ -9,7 +9,7 @@ export const api = { if (!headers.has('Accept')) headers.set('Accept', 'application/json'); if (options.body && !headers.has('Content-Type')) headers.set('Content-Type', 'application/json'); - const res = await fetch(url, { credentials: 'same-origin', ...options, headers }); + const res = await fetch(url, { credentials: 'include', ...options, headers }); if (!res.ok) { let responseData: any = null; diff --git a/admin-spa/src/routes/Login.tsx b/admin-spa/src/routes/Login.tsx index 14cd623..e44e166 100644 --- a/admin-spa/src/routes/Login.tsx +++ b/admin-spa/src/routes/Login.tsx @@ -37,6 +37,11 @@ export function Login() { window.WNW_CONFIG.currentUser = data.user; window.WNW_CONFIG.nonce = data.nonce; + // CRITICAL: Also update WNW_API.nonce for API requests + if (window.WNW_API) { + window.WNW_API.nonce = data.nonce; + } + // Redirect to dashboard (no reload needed, auth state is updated) navigate('/dashboard'); } else { diff --git a/includes/Api/AuthController.php b/includes/Api/AuthController.php index 72ef2dd..b2a5cff 100644 --- a/includes/Api/AuthController.php +++ b/includes/Api/AuthController.php @@ -31,8 +31,14 @@ class AuthController { ], 400 ); } - // Authenticate user - $user = wp_authenticate( $username, $password ); + // Use wp_signon to properly authenticate and set cookies (same as wp-login.php) + $credentials = [ + 'user_login' => $username, + 'user_password' => $password, + 'remember' => true, + ]; + + $user = wp_signon( $credentials, false ); if ( is_wp_error( $user ) ) { return new WP_REST_Response( [ @@ -43,15 +49,14 @@ class AuthController { // Check if user has WooCommerce permissions if ( ! user_can( $user, 'manage_woocommerce' ) ) { + // Logout if no permission + wp_logout(); return new WP_REST_Response( [ 'success' => false, 'message' => __( 'You do not have permission to access this area', 'woonoow' ), ], 403 ); } - // Set auth cookie - wp_set_auth_cookie( $user->ID, true ); - // Return user data and new nonce return new WP_REST_Response( [ 'success' => true,