SIMON Master Console Rebuild
True command center for SIMON + WEB360
This rebuild gives you a cleaner production spine: interactive tree, live viewer, guarded editor, dynamic graphics, architecture map, and note storage. It is designed to sit beside your existing
console.php
and become the stronger replacement path.
Dashboard
Tree
Viewer
System Map
File Registry
Notes
Files
15,026
Folders
377
Scanned Size
3.79 GB
PHP Files
791
Editable Text Files
12,310
File viewer
guarded to /htdocs
/api/auth/callback_guard.php
<?php declare(strict_types=1); /** * /api/auth/callback_guard.php * * Production OAuth callback handler for Google / Apple. * Google flow preserved as-is. * Apple flow fixed to generate a valid client secret JWT dynamically. */ error_reporting(E_ALL); ini_set('display_errors', '1'); ini_set('display_startup_errors', '1'); require_once __DIR__ . '/session_bootstrap.php'; function callback_guard_fail(string $message, int $status = 400, array $context = []): never { guardian_log_event('oauth_callback_fail', [ 'message' => $message, 'status' => $status, 'request_uri' => (string)($_SERVER['REQUEST_URI'] ?? ''), 'query_string' => (string)($_SERVER['QUERY_STRING'] ?? ''), 'method' => (string)($_SERVER['REQUEST_METHOD'] ?? ''), 'get_keys' => array_keys($_GET ?? []), 'post_keys' => array_keys($_POST ?? []), 'session_id' => session_id(), 'session_keys' => array_keys($_SESSION ?? []), 'oauth_state_keys' => array_keys(is_array($_SESSION['oauth_states'] ?? null) ? $_SESSION['oauth_states'] : []), 'context' => $context, ]); header('Content-Type: text/plain; charset=utf-8'); http_response_code($status); echo $message; exit; } function callback_http_post_form(string $url, array $fields): array { $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_POSTFIELDS => http_build_query($fields), CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => false, CURLOPT_TIMEOUT => 20, CURLOPT_HTTPHEADER => [ 'Content-Type: application/x-www-form-urlencoded', ], ]); $response = curl_exec($ch); $status = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE); $error = curl_error($ch); curl_close($ch); if ($response === false) { return [ 'ok' => false, 'status' => $status, 'error' => $error !== '' ? $error : 'Unknown cURL POST error', 'body' => null, 'json' => null, ]; } $json = json_decode($response, true); return [ 'ok' => $status >= 200 && $status < 300, 'status' => $status, 'error' => $status >= 200 && $status < 300 ? '' : ('HTTP ' . $status), 'body' => $response, 'json' => is_array($json) ? $json : null, ]; } function callback_http_get_json(string $url, array $headers = []): array { $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => false, CURLOPT_TIMEOUT => 20, CURLOPT_HTTPHEADER => $headers, ]); $response = curl_exec($ch); $status = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE); $error = curl_error($ch); curl_close($ch); if ($response === false) { return [ 'ok' => false, 'status' => $status, 'error' => $error !== '' ? $error : 'Unknown cURL GET error', 'body' => null, 'json' => null, ]; } $json = json_decode($response, true); return [ 'ok' => $status >= 200 && $status < 300, 'status' => $status, 'error' => $status >= 200 && $status < 300 ? '' : ('HTTP ' . $status), 'body' => $response, 'json' => is_array($json) ? $json : null, ]; } function callback_base64url_encode(string $input): string { return rtrim(strtr(base64_encode($input), '+/', '-_'), '='); } function callback_base64url_decode(string $input): string { $remainder = strlen($input) % 4; if ($remainder > 0) { $input .= str_repeat('=', 4 - $remainder); } return (string)base64_decode(strtr($input, '-_', '+/')); } function callback_decode_jwt_payload(string $jwt): array { $parts = explode('.', $jwt); if (count($parts) !== 3) { return []; } $payload = callback_base64url_decode($parts[1]); $json = json_decode($payload, true); return is_array($json) ? $json : []; } function callback_claim_bool(mixed $value): bool { if (is_bool($value)) { return $value; } if (is_int($value)) { return $value === 1; } if (is_string($value)) { return in_array(strtolower(trim($value)), ['1', 'true', 'yes'], true); } return false; } function callback_generate_apple_client_secret(): string { if ( !defined('APPLE_TEAM_ID') || !defined('APPLE_KEY_ID') || !defined('APPLE_CLIENT_ID') || !defined('APPLE_PRIVATE_KEY_PATH') ) { throw new RuntimeException('Apple JWT config is incomplete.'); } $privateKeyPath = (string)APPLE_PRIVATE_KEY_PATH; if ($privateKeyPath === '' || !is_file($privateKeyPath) || !is_readable($privateKeyPath)) { throw new RuntimeException('Apple private key file is missing or unreadable.'); } $privateKeyPem = file_get_contents($privateKeyPath); if ($privateKeyPem === false || trim($privateKeyPem) === '') { throw new RuntimeException('Apple private key file is empty.'); } $privateKey = openssl_pkey_get_private($privateKeyPem); if ($privateKey === false) { throw new RuntimeException('Apple private key could not be parsed.'); } $now = time(); $header = [ 'alg' => 'ES256', 'kid' => (string)APPLE_KEY_ID, 'typ' => 'JWT', ]; $payload = [ 'iss' => (string)APPLE_TEAM_ID, 'iat' => $now, 'exp' => $now + 86400 * 180, 'aud' => 'https://appleid.apple.com', 'sub' => (string)APPLE_CLIENT_ID, ]; $encodedHeader = callback_base64url_encode((string)json_encode($header, JSON_UNESCAPED_SLASHES)); $encodedPayload = callback_base64url_encode((string)json_encode($payload, JSON_UNESCAPED_SLASHES)); $signingInput = $encodedHeader . '.' . $encodedPayload; $signature = ''; $ok = openssl_sign($signingInput, $signature, $privateKey, OPENSSL_ALGO_SHA256); if (is_resource($privateKey) || $privateKey instanceof OpenSSLAsymmetricKey) { openssl_free_key($privateKey); } if (!$ok || $signature === '') { throw new RuntimeException('Failed to sign Apple client secret JWT.'); } return $signingInput . '.' . callback_base64url_encode($signature); } $provider = strtolower(trim((string)( $_GET['provider'] ?? $_POST['provider'] ?? $_SESSION['oauth_provider'] ?? '' ))); $code = trim((string)($_GET['code'] ?? $_POST['code'] ?? '')); $state = trim((string)($_GET['state'] ?? $_POST['state'] ?? '')); if ($state === '') { callback_guard_fail('Missing OAuth state.', 400); } $oauthStates = $_SESSION['oauth_states'] ?? []; if (!is_array($oauthStates)) { $oauthStates = []; } $stateRecord = $oauthStates[$state] ?? null; if (!is_array($stateRecord)) { callback_guard_fail('Invalid OAuth state.', 400); } $stateCreatedAt = (int)($stateRecord['created_at'] ?? 0); $stateProvider = strtolower(trim((string)($stateRecord['provider'] ?? ''))); $expectedNonce = (string)($stateRecord['nonce'] ?? ''); if ($provider === '') { $provider = $stateProvider; } if ($stateCreatedAt <= 0 || (time() - $stateCreatedAt) > 600) { unset($_SESSION['oauth_states'][$state]); callback_guard_fail('OAuth state expired.', 400); } if ($stateProvider !== '' && $provider !== '' && $stateProvider !== $provider) { unset($_SESSION['oauth_states'][$state]); callback_guard_fail('OAuth provider/state mismatch.', 400); } unset($_SESSION['oauth_states'][$state]); $resolvedEmail = ''; $emailVerified = false; if ($provider === 'google') { if ($code === '') { callback_guard_fail('Missing Google authorization code.', 400); } if (!defined('GOOGLE_CLIENT_ID') || !defined('GOOGLE_CLIENT_SECRET') || !defined('GOOGLE_REDIRECT_URI')) { callback_guard_fail('Google OAuth config is missing.', 500); } $tokenResponse = callback_http_post_form( 'https://oauth2.googleapis.com/token', [ 'code' => $code, 'client_id' => GOOGLE_CLIENT_ID, 'client_secret' => GOOGLE_CLIENT_SECRET, 'redirect_uri' => GOOGLE_REDIRECT_URI, 'grant_type' => 'authorization_code', ] ); if (!$tokenResponse['ok'] || !is_array($tokenResponse['json'])) { callback_guard_fail('Google token exchange failed.', 502, [ 'provider' => 'google', 'http_status' => $tokenResponse['status'] ?? 0, 'error' => $tokenResponse['error'] ?? '', 'response_body' => $tokenResponse['body'] ?? '', ]); } $tokenJson = $tokenResponse['json']; $accessToken = (string)($tokenJson['access_token'] ?? ''); $idToken = (string)($tokenJson['id_token'] ?? ''); if ($idToken !== '') { $claims = callback_decode_jwt_payload($idToken); $tokenNonce = (string)($claims['nonce'] ?? ''); if ($expectedNonce !== '' && $tokenNonce !== '' && !hash_equals($expectedNonce, $tokenNonce)) { callback_guard_fail('Invalid Google nonce.', 400); } $resolvedEmail = strtolower(trim((string)($claims['email'] ?? ''))); $emailVerified = callback_claim_bool($claims['email_verified'] ?? false); } if ($resolvedEmail === '' && $accessToken !== '') { $userInfo = callback_http_get_json( 'https://www.googleapis.com/oauth2/v2/userinfo', [ 'Authorization: Bearer ' . $accessToken, ] ); if ($userInfo['ok'] && is_array($userInfo['json'])) { $userJson = $userInfo['json']; $resolvedEmail = strtolower(trim((string)($userJson['email'] ?? ''))); $emailVerified = callback_claim_bool($userJson['verified_email'] ?? false); } } } elseif ($provider === 'apple') { if ($code === '') { callback_guard_fail('Missing Apple authorization code.', 400); } if (!defined('APPLE_CLIENT_ID') || !defined('APPLE_REDIRECT_URI')) { callback_guard_fail('Apple OAuth config is missing.', 500); } try { $appleClientSecret = callback_generate_apple_client_secret(); } catch (Throwable $e) { callback_guard_fail('Apple client secret generation failed.', 500, [ 'provider' => 'apple', 'exception' => $e->getMessage(), ]); } $tokenResponse = callback_http_post_form( 'https://appleid.apple.com/auth/token', [ 'client_id' => APPLE_CLIENT_ID, 'client_secret' => $appleClientSecret, 'code' => $code, 'grant_type' => 'authorization_code', 'redirect_uri' => APPLE_REDIRECT_URI, ] ); if (!$tokenResponse['ok'] || !is_array($tokenResponse['json'])) { callback_guard_fail('Apple token exchange failed.', 502, [ 'provider' => 'apple', 'http_status' => $tokenResponse['status'] ?? 0, 'error' => $tokenResponse['error'] ?? '', 'response_body' => $tokenResponse['body'] ?? '', ]); } $tokenJson = $tokenResponse['json']; $idToken = (string)($tokenJson['id_token'] ?? ''); if ($idToken === '') { callback_guard_fail('Missing Apple ID token.', 400); } $claims = callback_decode_jwt_payload($idToken); $tokenNonce = (string)($claims['nonce'] ?? ''); if ($expectedNonce !== '' && $tokenNonce !== '' && !hash_equals($expectedNonce, $tokenNonce)) { callback_guard_fail('Invalid Apple nonce.', 400); } $resolvedEmail = strtolower(trim((string)($claims['email'] ?? ''))); $emailVerified = callback_claim_bool($claims['email_verified'] ?? false); if ($resolvedEmail === '' && !empty($_POST['user'])) { $userJson = json_decode((string)$_POST['user'], true); if (is_array($userJson)) { $resolvedEmail = strtolower(trim((string)($userJson['email'] ?? ''))); } } if ($resolvedEmail !== '' && !$emailVerified) { $emailVerified = true; } } else { callback_guard_fail('Unsupported or missing OAuth provider.', 400); } if ($resolvedEmail === '') { callback_guard_fail('No authenticated email was resolved in callback.', 400); } if (!$emailVerified) { callback_guard_fail('Resolved email is not verified.', 403); } if (!guardian_email_is_allowed($resolvedEmail)) { callback_guard_fail('Access denied for this email.', 403); } $_SESSION['guardian_authenticated'] = true; $_SESSION['authenticated'] = true; $_SESSION['guardian_user'] = $resolvedEmail; $_SESSION['guardian_email'] = $resolvedEmail; $_SESSION['email'] = $resolvedEmail; $_SESSION['provider'] = $provider; $_SESSION['guardian_provider'] = $provider; $_SESSION['guardian_login_time'] = time(); $_SESSION['guardian_last_activity'] = time(); $_SESSION['guardian_risk_score'] = (int)($_SESSION['guardian_risk_score'] ?? 0); $_SESSION['risk_score'] = (int)($_SESSION['guardian_risk_score'] ?? 0); guardian_assign_role_from_email($resolvedEmail); session_regenerate_id(true); $role = guardian_current_role(); $defaultRedirect = $role === 'admin' ? (defined('GUARDIAN_POST_AUTH_DEFAULT_ADMIN') ? GUARDIAN_POST_AUTH_DEFAULT_ADMIN : '/_guardian/index.php') : (defined('GUARDIAN_POST_AUTH_DEFAULT_USER') ? GUARDIAN_POST_AUTH_DEFAULT_USER : '/simon/index.php'); $requestedRedirect = (string)( $_SESSION['guardian_post_login_redirect'] ?? $_SESSION['post_auth_redirect'] ?? '' ); $redirect = guardian_safe_redirect($requestedRedirect, $defaultRedirect); unset($_SESSION['guardian_post_login_redirect']); unset($_SESSION['post_auth_redirect']); unset($_SESSION['pending_oauth_email']); unset($_SESSION['oauth_provider']); unset($_SESSION['oauth_started_at']); if (isset($_SESSION['oauth_states']) && is_array($_SESSION['oauth_states']) && $_SESSION['oauth_states'] === []) { unset($_SESSION['oauth_states']); } guardian_log_event('oauth_callback_success', [ 'provider' => $provider, 'email' => $resolvedEmail, 'role' => guardian_current_role(), 'redirect' => $redirect, 'session_id' => session_id(), ]); header('Location: ' . $redirect, true, 302); exit;
Save file
Quick jump
open a path
Open