'', 'provider' => $provider, 'action' => $action, 'user_id' => $user_id, 'redirect_to' => $redirect_to, 'callback' => $callback, 'issuer' => OAuthProxyProtocol::issuer($server_url), ], OAuthProxyProtocol::REPLAY_TTL, true); $updated = OAuthTransactionStore::update($request_id, 'proxy_client', static function (array $row) use ($request_id): array { $row['payload']['request_id'] = $request_id; return $row; }, true); if (!$updated) { OAuthTransactionStore::claim($request_id, 'proxy_client', true); return ''; } } catch (\Throwable $e) { return ''; } $params = [ 'v' => OAuthProxyProtocol::VERSION, 'request_id' => $request_id, 'provider' => $provider, 'action' => $action, 'agent_back_url' => $callback, 'ts' => (string) time(), ]; $params['sign'] = OAuthProxyProtocol::sign_request($params, $secret); return $server_url . '/oauth/proxy/' . rawurlencode($provider) . '/?' . http_build_query($params, '', '&', PHP_QUERY_RFC3986); } public function handle_proxy_callback(): void { if (!get_query_var('aksrt_proxy_callback')) { return; } $fields = OAuthProxyProtocol::callback_fields($_GET); $signature = sanitize_text_field((string) wp_unslash($_GET['sign'] ?? '')); $secret = OAuthProviderCatalog::proxy_secret(); $request_id = sanitize_text_field($fields['request_id']); $provider = sanitize_key($fields['provider']); $action = $fields['action'] === 'bind' ? 'bind' : 'login'; if ( OAuthProviderCatalog::proxy_mode() !== 'client' || $fields['v'] !== OAuthProxyProtocol::VERSION || !OAuthProxyProtocol::token_valid($request_id) || !OAuthProxyProtocol::token_valid($fields['jti']) || !OAuthProxyProtocol::timestamp_valid($fields['ts']) || !OAuthProxyProtocol::verify_callback($fields, $secret, $signature) ) { $this->redirect_error('proxy_response_invalid', $provider, $action); } $row = OAuthTransactionStore::peek($request_id, 'proxy_client', true); $payload = is_array($row['payload'] ?? null) ? $row['payload'] : []; $expected_provider = sanitize_key((string) ($payload['provider'] ?? '')); $expected_action = (string) ($payload['action'] ?? 'login') === 'bind' ? 'bind' : 'login'; $expected_issuer = (string) ($payload['issuer'] ?? ''); $issuer = OAuthProxyProtocol::issuer($fields['issuer']); if ( !$row || !hash_equals((string) ($payload['request_id'] ?? ''), $request_id) || !hash_equals($expected_provider, $provider) || !hash_equals($expected_action, $action) || $issuer === '' || !hash_equals($expected_issuer, $issuer) || !OAuthProviderCatalog::is_direct($provider) || !in_array($provider, OAuthProviderCatalog::proxy_client_providers(), true) ) { $this->redirect_error('proxy_response_mismatch', $expected_provider ?: $provider, $expected_action); } if ($action === 'bind' && (int) ($payload['user_id'] ?? 0) !== get_current_user_id()) { $this->redirect_error('state_user_mismatch', $provider, $action); } $jti_scope = 'proxy_client_jti:' . $issuer . ':' . $fields['jti']; $jti_lock = OAuthTransactionStore::acquire_lock($jti_scope, OAuthProxyProtocol::REPLAY_TTL); if ($jti_lock === false) { $this->redirect_error('proxy_replayed', $provider, $action); } $claimed = OAuthTransactionStore::claim($request_id, 'proxy_client', true); if (!$claimed) { $this->redirect_error('proxy_replayed', $provider, $action); } if ($fields['error'] !== '') { $this->redirect_error($this->public_error($fields['error']), $provider, $action); } $identity = $this->identity($provider, $fields); if ($identity['provider_id'] === '') { $this->redirect_error('proxy_identity_invalid', $provider, $action); } if ($action === 'bind') { $result = OAuthAccountService::bind(get_current_user_id(), $provider, $identity); if (is_wp_error($result)) { $this->redirect_error( $result->get_error_code() === 'oauth_binding_conflict' ? 'binding_conflict' : 'binding_failed', $provider, $action ); } $this->redirect_success('bind', $provider, $this->settings_url()); } $user = OAuthAccountService::resolve($provider, $identity); if (is_wp_error($user) && $user->get_error_code() === 'oauth_no_match') { try { $obk = OAuthTransactionStore::issue('obk', [ 'provider' => $provider, 'provider_id' => $identity['provider_id'], 'user_data' => $identity, 'redirect_to' => OAuthUrlPolicy::validated_redirect((string) ($payload['redirect_to'] ?? ''), home_url('/')), ], 15 * MINUTE_IN_SECONDS, true); } catch (\Throwable $e) { $this->redirect_error('account_match_failed', $provider, $action); } $bind_url = function_exists('aksrt_get_bind_url') ? aksrt_get_bind_url($obk) : home_url('/sign/?tab=bind&obk=' . rawurlencode($obk)); wp_safe_redirect($bind_url); exit; } if (!$user instanceof \WP_User) { $this->redirect_error('account_match_failed', $provider, $action); } OAuthAccountService::complete_login($user, true); $this->redirect_success( 'login', $provider, OAuthUrlPolicy::validated_redirect((string) ($payload['redirect_to'] ?? ''), home_url('/')) ); } private function identity(string $provider, array $fields): array { return [ 'provider' => $provider, 'provider_id' => sanitize_text_field($fields['provider_id']), 'username' => sanitize_text_field($fields['username']), 'name' => sanitize_text_field($fields['name']), 'email' => sanitize_email($fields['email']), 'email_verified' => $fields['email_verified'] === '1', 'avatar_url' => esc_url_raw($fields['avatar_url']), 'url' => esc_url_raw($fields['url']), 'bio' => sanitize_text_field($fields['bio']), 'locale' => sanitize_text_field($fields['locale']), ]; } private function public_error(string $error): string { $error = sanitize_key($error); $allowed = [ 'provider_cancelled', 'code_missing', 'provider_unavailable', 'identity_invalid', 'callback_failed', ]; return in_array($error, $allowed, true) ? $error : 'callback_failed'; } private function settings_url(): string { return function_exists('aksrt_get_user_center_url') ? aksrt_get_user_center_url('settings') : home_url('/'); } private function redirect_success(string $action, string $provider, string $target): void { wp_safe_redirect(add_query_arg([ 'oauth_action' => $action, 'oauth_provider' => sanitize_key($provider), ], $target)); exit; } private function redirect_error(string $code, string $provider = '', string $action = 'login'): void { $target = $action === 'bind' && is_user_logged_in() ? $this->settings_url() : (function_exists('aksrt_get_login_url') ? aksrt_get_login_url() : wp_login_url()); wp_safe_redirect(add_query_arg([ 'oauth_action' => 'error', 'oauth_provider' => sanitize_key($provider), 'oauth_error' => sanitize_key($code), ], $target)); exit; } }