Embed::adminOrigin(), 'return_url' => admin_url('admin-post.php?action=' . self::RETURN_ACTION), 'site_name' => self::siteName(), 'site_url' => home_url('/'), ]; if ($kind === 'account') { $params['account_only'] = '1'; $params['site_key'] = (string) Options::get('site_key'); } if (in_array($from, self::FROM, true)) { $params['from'] = $from; } return self::homeUrl() . $path . '?' . http_build_query($params, '', '&', PHP_QUERY_RFC3986); } public static function siteName(): string { return html_entity_decode(wp_strip_all_tags((string) get_bloginfo('name')), ENT_QUOTES | ENT_HTML5, 'UTF-8'); } public const ALLOWED_HTML = [ 'button' => ['class' => true, 'name' => true, 'type' => true, 'value' => true], 'form' => ['action' => true, 'class' => true, 'method' => true], 'input' => ['id' => true, 'name' => true, 'type' => true, 'value' => true], ]; public static function button(string $kind, string $label, string $class = 'button button-primary', string $from = ''): string { return sprintf( '
%s%s
', esc_url(admin_url('admin-post.php')), wp_nonce_field(self::START_ACTION, '_wpnonce', true, false), esc_attr(self::START_ACTION), in_array($from, self::FROM, true) ? sprintf('', esc_attr($from)) : '', esc_attr($kind), esc_attr($class), esc_html($label), ); } public function start(): void { check_admin_referer(self::START_ACTION); $requested = isset($_POST['kind']) ? sanitize_key(wp_unslash($_POST['kind'])) : ''; $kind = in_array($requested, ['signup', 'account'], true) ? $requested : 'connect'; if ($kind === 'account' && (string) Options::get('site_key') === '') { $kind = 'connect'; } $from = isset($_POST['from']) ? sanitize_key(wp_unslash($_POST['from'])) : ''; self::requireCapability($kind); set_transient(self::pendingKey(), $kind, self::PENDING_SEC); // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect -- the Sarcio service lives on its own domain. wp_redirect(self::destination($kind, $from)); exit; } public function finish(): void { $pending = get_transient(self::pendingKey()); delete_transient(self::pendingKey()); $kind = in_array($pending, ['account', 'connect', 'signup'], true) ? $pending : 'connect'; self::requireCapability($kind); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- the one-time code is the credential, and the pending marker set by start() proves this user began the connection. $code = isset($_GET['sarcio_code']) ? sanitize_text_field(wp_unslash((string) $_GET['sarcio_code'])) : ''; if (!$pending || $code === '') { self::back('expired', $kind); } $response = wp_remote_post(Options::apiUrl() . '/api/connect/exchange', [ 'body' => (string) wp_json_encode(['code' => $code]), 'headers' => ['Content-Type' => 'application/json'], 'redirection' => 0, 'timeout' => 10, ]); $status = is_wp_error($response) ? 0 : (int) wp_remote_retrieve_response_code($response); $body = $status === 0 ? null : json_decode((string) wp_remote_retrieve_body($response), true); if (($status !== 200 && $status !== 201) || !is_array($body) || !preg_match('~^pk_[A-Za-z0-9_-]{1,128}$~', (string) ($body['siteKey'] ?? ''))) { self::back($status === 409 ? 'keys' : 'failed', $kind); } $secret = (string) ($body['secretKey'] ?? ''); if ($secret === '') { if ((string) $body['siteKey'] !== (string) Options::get('site_key')) { self::back('failed', $kind); } } else { if (!current_user_can('manage_options') || !preg_match('~^sk_[A-Za-z0-9_-]{1,128}$~', $secret)) { self::back('failed', $kind); } $changes = [ 'review_in_admin' => true, 'sidecar_key' => $secret, 'site_key' => (string) $body['siteKey'], ]; Options::update($changes); Embed::info(true); Usage::forget(); } Brand::store(is_array($body['branding'] ?? null) ? $body['branding'] : null); Account::store(get_current_user_id(), $body); wp_safe_redirect(add_query_arg('sarcio-connected', $secret === '' ? 'account' : '1', admin_url('admin.php?page=' . ReviewPage::SLUG))); exit; } private static function requireCapability(string $kind): void { $capability = $kind === 'account' ? ReviewPage::CAPABILITY : 'manage_options'; if (!current_user_can($capability)) { wp_die(esc_html__('Only an administrator can connect this site.', 'sarcio'), 403); } } private static function back(string $reason, string $kind): never { $page = $kind === 'account' ? 'admin.php?page=' . ReviewPage::SLUG : 'options-general.php?page=' . SettingsPage::SLUG; wp_safe_redirect(add_query_arg('sarcio-connect-error', $reason, admin_url($page))); exit; } private static function pendingKey(): string { return 'sarcio_connect_pending_' . get_current_user_id(); } }