Review selected server packages. You can configure and add servers before logging in; login or account creation is required when you proceed to checkout.
+
+
+
+
+
+
+
= website_escape($error) ?>
+
+
+
= website_escape($message) ?>
+
+
+
+
+
Your cart is empty
+
Browse the catalog to choose a game server, slots, and location.
Prices and service availability are revalidated server-side before payment or provisioning. Adding an item to the cart does not create a running server.
After login, Control Panel and server-management links use a short-lived one-time SSO handoff. Passwords and PHP session IDs are never passed between domains.
+
Gameservers.World and the GSP Panel use the same account credentials, but they keep separate secure sessions. You may be asked to log in separately when opening the Panel.
= website_escape($description !== '' ? $description : 'Review this server plan before checkout. Pricing and provisioning are validated server-side from the active catalog.') ?>
+
= website_escape($description !== '' ? $description : 'Configure this server package before adding it to your cart. Login is only required when you proceed to checkout.') ?>
This website validates the catalog service and keeps your shared account identity. Payment and final provisioning must complete server-side before the Panel creates a running game server.
+
You can add this server to your cart before logging in. Payment and final provisioning must complete server-side before the Panel creates a running game server.
The legacy billing/order.php route is no longer used.
-
- Checkout/payment handlers are not present in this repository checkout. Contact support to complete this order or connect the active payment module before enabling public checkout.
-
diff --git a/Panel/modules/website/sso.php b/Panel/modules/website/sso.php
index 1ab69c68..4cf716a5 100644
--- a/Panel/modules/website/sso.php
+++ b/Panel/modules/website/sso.php
@@ -4,78 +4,15 @@ declare(strict_types=1);
require_once __DIR__ . '/includes/bootstrap.php';
-$db = website_db();
-$prefix = website_table_prefix();
+$destination = trim((string)($_GET['destination'] ?? 'panel'));
+$returnPath = website_safe_return_path((string)($_GET['return'] ?? ''), 'home.php?m=dashboard&p=dashboard');
-if (!$db instanceof mysqli || $prefix === '') {
- http_response_code(503);
- echo 'SSO is temporarily unavailable.';
+website_log_activity('Deprecated website SSO endpoint redirected to direct login/navigation', (int)($_SESSION['website_user_id'] ?? 0), 'sso_deprecated_redirect');
+
+if ($destination === 'panel') {
+ header('Location: ' . panel_url($returnPath), true, 302);
exit;
}
-$token = trim((string)($_GET['token'] ?? ''));
-if ($token !== '') {
- if (!function_exists('gsp_sso_validate_token')) {
- http_response_code(503);
- echo 'SSO is not configured.';
- exit;
- }
-
- $validation = gsp_sso_validate_token($db, $prefix, $token, 'website');
- if (empty($validation['success'])) {
- website_log_activity('Website SSO failed: ' . (string)$validation['error'], 0, 'sso_failure');
- http_response_code(403);
- echo website_escape((string)$validation['error']);
- exit;
- }
-
- $user = website_panel_user_by_id((int)$validation['user_id']);
- if (!$user) {
- http_response_code(403);
- echo 'SSO user is no longer available.';
- exit;
- }
-
- website_set_user_session($user);
- website_log_activity('Website SSO login succeeded for ' . (string)$user['users_login'], (int)$user['user_id'], 'sso_success');
- $returnPath = website_safe_return_path((string)$validation['return_path'], 'index.php');
- header('Location: ' . website_url($returnPath), true, 302);
- exit;
-}
-
-$destination = trim((string)($_GET['destination'] ?? ''));
-if ($destination !== 'panel') {
- http_response_code(400);
- echo 'Invalid SSO destination.';
- exit;
-}
-
-$user = website_current_user();
-if (!$user) {
- $_SESSION['website_login_return'] = 'panel';
- header('Location: ' . website_login_url('panel'), true, 302);
- exit;
-}
-
-if (!function_exists('gsp_sso_create_token')) {
- http_response_code(503);
- echo 'SSO is not configured.';
- exit;
-}
-
-$returnPath = website_safe_return_path((string)($_GET['return'] ?? 'home.php?m=dashboard&p=dashboard'), 'home.php?m=dashboard&p=dashboard');
-$newToken = gsp_sso_create_token($db, $prefix, (int)$user['user_id'], 'website', 'panel', $returnPath, 60);
-if ($newToken === null) {
- http_response_code(403);
- echo 'SSO requires HTTPS.';
- exit;
-}
-
-website_log_activity('Website-to-panel SSO token created', (int)$user['user_id'], 'sso_token_created');
-$panelSsoUrl = trim((string)website_config('panel_sso_url', ''));
-if ($panelSsoUrl === '') {
- $panelSsoUrl = panel_url('sso.php');
-}
-
-header('Location: ' . $panelSsoUrl . '?token=' . rawurlencode($newToken), true, 302);
+header('Location: ' . website_url('index.php'), true, 302);
exit;
diff --git a/Panel/sso.php b/Panel/sso.php
index 66cadf37..8f5e2767 100644
--- a/Panel/sso.php
+++ b/Panel/sso.php
@@ -3,139 +3,19 @@
declare(strict_types=1);
require_once __DIR__ . '/includes/functions.php';
-require_once __DIR__ . '/includes/helpers.php';
-require_once __DIR__ . '/includes/html_functions.php';
-require_once __DIR__ . '/includes/sso.php';
-startSession();
+$destination = trim((string)($_GET['destination'] ?? ''));
+$returnPath = trim((string)($_GET['return'] ?? 'serverlist.php'));
-define('CONFIG_FILE', __DIR__ . '/includes/config.inc.php');
-require_once CONFIG_FILE;
-
-$mysqli = @mysqli_connect($db_host, $db_user, $db_pass, $db_name, isset($db_port) ? (int)$db_port : null);
-if (!$mysqli instanceof mysqli) {
- http_response_code(503);
- echo 'SSO is temporarily unavailable.';
- exit;
-}
-@mysqli_set_charset($mysqli, 'utf8mb4');
-
-$prefix = (string)$table_prefix;
-$panelReturnDefault = 'home.php?m=dashboard&p=dashboard';
-$websiteBase = function_exists('gsp_website_url') ? gsp_website_url() : 'https://gameservers.world/';
-
-function gsp_panel_sso_user(mysqli $db, string $prefix, int $userId): ?array
-{
- $table = $db->real_escape_string($prefix . 'users');
- $stmt = $db->prepare("SELECT * FROM `{$table}` WHERE `user_id` = ? LIMIT 1");
- if (!$stmt) {
- return null;
- }
- $stmt->bind_param('i', $userId);
- $stmt->execute();
- $result = $stmt->get_result();
- $user = $result instanceof mysqli_result ? $result->fetch_assoc() : null;
- $stmt->close();
- return is_array($user) ? $user : null;
+$returnPath = ltrim($returnPath, '/');
+if ($returnPath === '' || preg_match('#^[a-z][a-z0-9+.-]*://#i', $returnPath) === 1 || str_starts_with($returnPath, '//') || str_contains($returnPath, "\0") || str_starts_with($returnPath, '../') || str_contains($returnPath, '/../')) {
+ $returnPath = 'serverlist.php';
}
-function gsp_panel_sso_api_token(mysqli $db, string $prefix, int $userId): string
-{
- $table = $db->real_escape_string($prefix . 'api_tokens');
- $db->query(
- "CREATE TABLE IF NOT EXISTS `{$table}` (
- `user_id` int(11) NOT NULL,
- `token` varchar(64) NOT NULL,
- PRIMARY KEY (`user_id`),
- UNIQUE KEY `user_id` (`user_id`)
- ) ENGINE=MyISAM DEFAULT CHARSET=latin1"
- );
-
- $stmt = $db->prepare("SELECT `token` FROM `{$table}` WHERE `user_id` = ? LIMIT 1");
- if ($stmt) {
- $stmt->bind_param('i', $userId);
- $stmt->execute();
- $result = $stmt->get_result();
- $row = $result instanceof mysqli_result ? $result->fetch_assoc() : null;
- $stmt->close();
- if (is_array($row) && !empty($row['token'])) {
- return (string)$row['token'];
- }
- }
-
- $token = bin2hex(random_bytes(32));
- $insert = $db->prepare("INSERT INTO `{$table}` (`user_id`, `token`) VALUES (?, ?) ON DUPLICATE KEY UPDATE `token` = VALUES(`token`)");
- if ($insert) {
- $insert->bind_param('is', $userId, $token);
- $insert->execute();
- $insert->close();
- }
-
- return $token;
-}
-
-function gsp_panel_sso_log(mysqli $db, string $prefix, int $userId, string $message): void
-{
- $table = $db->real_escape_string($prefix . 'logger');
- $ip = $db->real_escape_string(gsp_sso_client_ip());
- $message = $db->real_escape_string(substr($message, 0, 1000));
- $db->query(
- "INSERT INTO `{$table}` (`date`, `user_id`, `ip`, `message`, `source_type`, `category`, `event_type`, `severity`)
- VALUES (FROM_UNIXTIME(UNIX_TIMESTAMP(), '%d-%m-%Y %H:%i:%s'), {$userId}, '{$ip}', '{$message}', 'sso', 'authentication', 'sso', 'info')"
- );
-}
-
-$token = trim((string)($_GET['token'] ?? ''));
-if ($token !== '') {
- $validation = gsp_sso_validate_token($mysqli, $prefix, $token, 'panel');
- if (empty($validation['success'])) {
- http_response_code(403);
- echo htmlspecialchars((string)$validation['error'], ENT_QUOTES, 'UTF-8');
- exit;
- }
-
- $user = gsp_panel_sso_user($mysqli, $prefix, (int)$validation['user_id']);
- if (!$user) {
- http_response_code(403);
- echo 'SSO user is no longer available.';
- exit;
- }
-
- session_regenerate_id(true);
- $_SESSION['user_id'] = $user['user_id'];
- $_SESSION['users_login'] = $user['users_login'];
- $_SESSION['users_passwd'] = $user['users_passwd'];
- $_SESSION['users_group'] = $user['users_role'];
- $_SESSION['users_lang'] = $user['users_lang'];
- $_SESSION['users_theme'] = $user['users_theme'];
- $_SESSION['users_api_key'] = gsp_panel_sso_api_token($mysqli, $prefix, (int)$user['user_id']);
-
- gsp_panel_sso_log($mysqli, $prefix, (int)$user['user_id'], 'Panel SSO login succeeded for ' . $user['users_login']);
- $returnPath = gsp_sso_safe_return_path((string)$validation['return_path'], $panelReturnDefault);
- header('Location: ' . $returnPath, true, 302);
+if ($destination === 'website') {
+ header('Location: ' . gsp_website_url($returnPath), true, 302);
exit;
}
-$destination = trim((string)($_GET['destination'] ?? 'website'));
-if ($destination !== 'website') {
- http_response_code(400);
- echo 'Invalid SSO destination.';
- exit;
-}
-
-if (empty($_SESSION['user_id'])) {
- header('Location: index.php', true, 302);
- exit;
-}
-
-$returnPath = gsp_sso_safe_return_path((string)($_GET['return'] ?? 'serverlist.php'), 'serverlist.php');
-$newToken = gsp_sso_create_token($mysqli, $prefix, (int)$_SESSION['user_id'], 'panel', 'website', $returnPath, 60);
-if ($newToken === null) {
- http_response_code(403);
- echo 'SSO requires HTTPS.';
- exit;
-}
-
-gsp_panel_sso_log($mysqli, $prefix, (int)$_SESSION['user_id'], 'Panel-to-website SSO token created');
-header('Location: ' . rtrim($websiteBase, '/') . '/sso.php?token=' . rawurlencode($newToken), true, 302);
+header('Location: index.php', true, 302);
exit;
diff --git a/docs/architecture/API_REFERENCE.md b/docs/architecture/API_REFERENCE.md
index 9e1ec448..452d8700 100644
--- a/docs/architecture/API_REFERENCE.md
+++ b/docs/architecture/API_REFERENCE.md
@@ -40,9 +40,9 @@ public node status
website account/order entry
-> Panel/modules/website/login.php
- -> Panel/modules/website/sso.php and Panel/sso.php
+ -> Panel/modules/website/sso.php and Panel/sso.php compatibility redirects
-> Panel/modules/website/order.php
- -> shared users table and one-time SSO token table
+ -> shared users table, separate website and Panel sessions
```
## Panel -> Agent XML-RPC
@@ -168,14 +168,15 @@ Return shape:
- `mem_percent`
- `disk_percent`
-## Website Account, SSO, And Order Entry
+## Website Account And Order Entry
| Endpoint | Purpose | Auth / Verification |
|---|---|---|
| `Panel/modules/website/login.php` | create website session from shared Panel user database | username/password checked against Panel hash format |
-| `Panel/modules/website/sso.php` | website SSO endpoint | website session or one-time SSO token |
-| `Panel/sso.php` | Panel SSO endpoint | Panel session or one-time SSO token |
-| `Panel/modules/website/order.php` | validate `service_id` and start order intent | website session for continuation |
+| `Panel/modules/website/sso.php` | compatibility redirect for old SSO links | no token/session creation |
+| `Panel/sso.php` | compatibility redirect for old SSO links | no token/session creation |
+| `Panel/modules/website/order.php` | validate `service_id`, slots, and location before adding to cart | anonymous website session |
+| `Panel/modules/website/cart.php` | review cart and require login only at checkout | anonymous website session; website login for checkout |
The old `Website/api/*` and `Website/webhook.php` checkout compatibility files are not present in this checkout. Payment processing must be reconnected and documented before public checkout is enabled.
diff --git a/docs/features/USER_API.md b/docs/features/USER_API.md
index 8e0a91b5..7a82f379 100644
--- a/docs/features/USER_API.md
+++ b/docs/features/USER_API.md
@@ -120,21 +120,20 @@ The scheduler does not call agents directly at runtime. It stores cron lines on
This makes `ogp_api.php` part of the internal scheduler runtime contract.
-## Website Account, SSO, And Order Entry
+## Website Account And Order Entry
| Endpoint | Auth | Purpose | Parameters | Returns |
|---|---|---|---|---|
| `Panel/modules/website/login.php` | Panel user credentials | create a website session against the shared Panel user table | username/password form | website session and redirect |
| `Panel/modules/website/logout.php` | website session | destroy website session | none | redirect to website home |
-| `Panel/modules/website/sso.php?destination=panel` | website session | create a one-time token for Panel login | optional trusted return path | redirect to `Panel/sso.php` |
-| `Panel/sso.php?token=...` | one-time SSO token | create normal Panel session | token | redirect to Panel page |
-| `Panel/sso.php?destination=website` | Panel session | create a one-time token for website login | optional trusted return path | redirect to website SSO endpoint |
-| `Panel/modules/website/sso.php?token=...` | one-time SSO token | create website session | token | redirect to website page |
-| `Panel/modules/website/order.php` | website session for checkout continuation | validate catalog service and start order intent | `service_id` | order page or login redirect |
+| `Panel/modules/website/sso.php` | none | compatibility redirect for old SSO links | safe `destination` / `return` values | direct website or Panel redirect |
+| `Panel/sso.php` | none | compatibility redirect for old Panel-to-website SSO links | safe `destination` / `return` values | direct website or Panel redirect |
+| `Panel/modules/website/order.php` | anonymous website session | validate catalog service and configure order intent | `service_id`, slots/location POST | order page or cart redirect |
+| `Panel/modules/website/cart.php` | anonymous website session; website login required only for checkout | review cart and begin checkout intent | cart actions | cart page or login redirect |
-SSO tokens are stored in `OGP_DB_PREFIXsso_tokens` as SHA-256 hashes, expire in 30-60 seconds, and are marked used after successful validation. Tokens never contain passwords, password hashes, permanent API keys, or PHP session IDs.
+SSO is deferred in the current implementation because `gameservers.world` and `panel.iaregamer.com` cannot share one PHP session cookie. Users can use the same Panel-backed credentials on both sites, but website and Panel sessions are separate.
-The old `Website/api/create_order.php`, `Website/api/capture_order.php`, `Website/api/log_error.php`, and `Website/webhook.php` compatibility files are not present in this repository checkout. Until an active payment runtime is connected, the website order page validates service intent and sends customers to support rather than claiming checkout is complete.
+The old `Website/api/create_order.php`, `Website/api/capture_order.php`, `Website/api/log_error.php`, and `Website/webhook.php` compatibility files are not present in this repository checkout. Until an active payment runtime is connected, the website cart preserves validated order intent and displays a friendly checkout-unavailable message rather than claiming checkout is complete.
### Webhooks
@@ -149,7 +148,7 @@ The old `Website/api/create_order.php`, `Website/api/capture_order.php`, `Websit
| token auth | `Panel/ogp_api.php` |
| host allowlist | `api_authorized.hosts`, `api_authorized.fwd_hosts`, `settings/api_hosts.php` |
| role / ownership checks | inside `api_*` handlers in `ogp_api.php` |
-| one-time SSO token hash storage | `OGP_DB_PREFIXsso_tokens` |
+| website session cart | `$_SESSION['website_cart']` |
## Search Coverage Used For This Document
diff --git a/docs/modules/billing.md b/docs/modules/billing.md
index f6fa08f6..132b1d7e 100644
--- a/docs/modules/billing.md
+++ b/docs/modules/billing.md
@@ -42,11 +42,11 @@ Commercial billing, provisioning, invoices, orders, transactions, coupons, and p
## Website Ordering Boundary
-The active Gameservers.World website no longer links customers to `billing/order.php`. The public catalog uses `Panel/modules/website/order.php?service_id=...` as the order entry point. That page validates the enabled service server-side and sends logged-out users through website login before returning them to the intended service.
+The active Gameservers.World website no longer links customers to `billing/order.php`. The public catalog uses `Panel/modules/website/order.php?service_id=...` as the order entry point. That page validates the enabled service server-side and allows anonymous visitors to configure slots/location and add the package to the website session cart.
Payment approval and final provisioning remain server-side responsibilities. The browser must not call private provisioning methods directly, and prices must be read from server-side catalog data rather than query parameters.
-In this repository checkout the historical `Panel/modules/billing` runtime is not present, although billing tables and integration references remain. The website order page therefore stops at validated order intent and support handoff until the active checkout/payment runtime is connected.
+In this repository checkout the historical `Panel/modules/billing` runtime is not present, although billing tables and integration references remain. The website cart therefore stops at validated order intent and a friendly checkout-unavailable message until the active checkout/payment runtime is connected.
## Admin Workflow
diff --git a/docs/modules/website.md b/docs/modules/website.md
index 911e4b57..ec16f2df 100644
--- a/docs/modules/website.md
+++ b/docs/modules/website.md
@@ -35,24 +35,15 @@ The website module centralizes these helpers in `includes/bootstrap.php`:
The website does not include the billing config loader directly. It reads panel or billing DB values safely, uses them only when needed, and avoids public fatal errors tied to missing config files.
-## Shared Accounts and SSO
+## Shared Accounts
The website uses the Panel `users` table as the account source of truth. A customer has the same `user_id` on Gameservers.World, the GSP Panel, support, billing, and server orders.
Website login verifies credentials against the existing Panel password hash format. This preserves current Panel login behavior and avoids a second website password database.
-`gameservers.world` and `panel.iaregamer.com` cannot share a normal PHP session cookie because they are unrelated parent domains. The bridge is a one-time SSO token:
+`gameservers.world` and `panel.iaregamer.com` cannot share a normal PHP session cookie because they are unrelated parent domains. SSO is deferred for this phase. The website and Panel keep separate sessions, and users may log in separately on both sites with the same credentials. Passwords, password hashes, PHP session IDs, and authentication tokens are never passed in URLs.
-- website to Panel: `Panel/modules/website/sso.php` creates a token and redirects to `Panel/sso.php`
-- Panel to website: `Panel/sso.php` creates a token and redirects back to `Panel/modules/website/sso.php`
-- table: `OGP_DB_PREFIXsso_tokens`
-- lifetime: 30-60 seconds
-- storage: SHA-256 token hash only
-- reuse: rejected after `used_at` is set
-- URL contents: token only, never passwords, password hashes, API keys, or PHP session IDs
-- HTTPS is required in production
-
-Expired tokens are cleaned opportunistically when SSO is used. The administration module also creates the table for fresh installs.
+`Panel/modules/website/sso.php` and `Panel/sso.php` are retained only as compatibility redirects for old links. Active navigation must not depend on them.
## Ordering
@@ -62,11 +53,13 @@ The current public catalog route is `serverlist.php`. Customer-facing Order butt
The old `billing/order.php` route is obsolete in this repository layout and must not be used for active Gameservers.World links.
-`order.php` validates the requested `service_id` server-side against enabled catalog records before allowing the customer to continue. Logged-out customers have the intended order path stored in the website session, are sent to `login.php`, and return to the same service after successful login.
+`order.php` validates the requested `service_id` server-side against enabled catalog records before allowing the customer to continue. Anonymous visitors can configure slots and location, add the server package to the session cart, and review the cart before login.
-The website owns catalog display, order intent, login-return behavior, checkout entry, and customer confirmation. The Panel owns final provisioning, server assignment to the shared `user_id`, game-home creation, agent handoff, and provisioning state. Public browser requests must not call private provisioning methods directly.
+Login or registration is required only at checkout. The cart is stored in the website session and remains available through website login session regeneration. Panel registration is currently linked directly until a website-native registration form is restored.
-Checkout/payment handlers are not present in this repository checkout. Until the active payment runtime is connected, `order.php` validates the selected service and sends the customer to support instead of pretending payment or provisioning is available.
+The website owns catalog display, cart storage, order intent, login-return behavior, checkout entry, and customer confirmation. The Panel owns final provisioning, server assignment to the shared `user_id`, game-home creation, agent handoff, and provisioning state. Public browser requests must not call private provisioning methods directly.
+
+Checkout/payment handlers are not present in this repository checkout. Until the active payment runtime is connected, `cart.php` preserves the validated cart and shows a friendly checkout-unavailable message instead of pretending payment or provisioning is available.
## Navigation
@@ -76,7 +69,7 @@ Website footer account links are state-aware:
- logged in: `My Account`, `Order a Server`, `Control Panel`, `My Servers`, `Log Out`
- staff-only links appear only for Panel admin users and still rely on Panel authorization server-side
-The website Control Panel button sends logged-in users through website-to-Panel SSO. Logged-out users go through website login first. The Panel dashboard `Order Another Server` link sends logged-in Panel users through Panel-to-website SSO.
+The website main navigation also includes visible `Login`, `Create Account`, and `Cart` entries when appropriate. Control Panel, My Servers, and staff administration links point directly to the configured Panel domain. The Panel dashboard `Order Another Server` link points directly to the website catalog.
## Deployment
@@ -101,6 +94,7 @@ Recommended:
- `login.php`
- `account.php`
- `order.php`
+- `cart.php`
- `sso.php`
## Pricing and Platform Reference