| | |
| | | { |
| | | use _Base, Admin; |
| | | protected bool $hasOAuth = true; |
| | | protected OAuthURLs $oauth; |
| | | /** |
| | | * Child classes must set this (e.g. 'https://api.example.com/v1/') |
| | | */ |
| | |
| | | protected array $oathScopes = []; |
| | | |
| | | |
| | | abstract protected function getOAuthRequestHeaders(): array; |
| | | abstract protected function handleOAuthResponse(array|WP_Error $response, string $method, string $endpoint): array; |
| | | /** |
| | | * @return array Defaults to the base getRequestHeaders, but can optionally be overridden |
| | | */ |
| | | protected function getOAuthRequestHeaders(): array |
| | | { |
| | | return $this->getRequestHeaders(); |
| | | } |
| | | |
| | | /** |
| | | * Default to the base handleResponse method, but extensions can modify accordingly |
| | | * @param array|WP_Error $response |
| | | * @param string $method |
| | | * @param string $endpoint |
| | | * @return array |
| | | */ |
| | | protected function handleOAuthResponse(array|WP_Error $response, string $method, string $endpoint): array |
| | | { |
| | | return $this->handleResponse($response, $method, $endpoint); |
| | | } |
| | | abstract protected function refreshAccessToken(OAuthCredentials $credentials): array; |
| | | |
| | | function addOAuthActions():void |
| | |
| | | 'icon' => 'plugs-disconnected', |
| | | 'action' => [$this, 'handleOAuthDisconnect'] |
| | | ], |
| | | [ |
| | | 'name' => 'Refresh Token', |
| | | 'icon' => 'arrows-clockwise', |
| | | 'action' => [$this, 'handleOAuthRefreshToken'] |
| | | ] |
| | | // [ |
| | | // 'name' => 'Refresh Token', |
| | | // 'icon' => 'arrows-clockwise', |
| | | // 'action' => [$this, 'handleOAuthRefreshToken'] |
| | | // ] |
| | | ]; |
| | | foreach ($actions as $a) { |
| | | $this->addAction($a['name'], $a['action'], $a['slug']??null, $a['icon']??null, $a['label']??null); |
| | | } |
| | | } |
| | | public function handleOAuthConnect():array |
| | | { |
| | | if (!$this->hasOAuth) { |
| | | return $this->response(false, 'Not an OAuth service'); |
| | | } |
| | | $url = $this->getOAuthUrl(); |
| | | if ($url) { |
| | | return [ |
| | | 'success' => true, |
| | | 'redirect' => $url, |
| | | ]; |
| | | } |
| | | return $this->response(false, 'Failed to generate OAuth URL'); |
| | | } |
| | | |
| | | public function handleOAuthDisconnect():array |
| | | { |
| | | $revoked = $this->revokeOAuth(); |
| | | return $this->response($revoked, $revoked ? |
| | | 'Successfully disconnected from '.$this->title : |
| | | 'Failed to disconnect from '.$this->title); |
| | | } |
| | | |
| | | public function revokeOAuth():bool |
| | | { |
| | | if (!$this->hasOAuth) { |
| | | return false; |
| | | } |
| | | if (empty($this->oauth['revoke'])) { |
| | | error_log('No revoke url set for '.$this->service_name); |
| | | return false; |
| | | } |
| | | $credentials = $this->loadCredentials(); |
| | | if (empty($credentials['access_token'])) { |
| | | error_log('Could not send revoke request, no access_token for '.$this->service_name); |
| | | } else { |
| | | $response = $this->postOAuthRequest($this->oauth['revoke'], [ |
| | | 'body' => [ |
| | | 'token' => $credentials['access_token'] |
| | | ] |
| | | ]); |
| | | $empty = new OAuthCredentials([]); |
| | | foreach ($empty->toArray() as $key => $value) { |
| | | if (array_key_exists($key, $credentials)) { |
| | | unset($credentials[$key]); |
| | | } |
| | | } |
| | | |
| | | return Auth::getInstance()->storeCredentials($this->service_name, $credentials, $this->userID); |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | protected function registerOAuthCallbacks():void |
| | | { |
| | |
| | | return $this->handleOAuthResponse($error, $method, $endpoint); |
| | | } |
| | | |
| | | if (!empty($this->requiredScopes) && !$credentials->hasScopes($this->requiredScopes)) { |
| | | $error = new WP_Error('insufficient_scope', "Missing required scopes for {$this->service_name}"); |
| | | return $this->handleOAuthResponse($error, $method, $endpoint); |
| | | } |
| | | |
| | | $url = rtrim($this->baseOAuthUrl, '/') . '/' . ltrim($endpoint, '/'); |
| | | |
| | |
| | | return ''; |
| | | } |
| | | $credentials = $this->loadCredentials(); |
| | | if (empty($credentials['authorize'])) { |
| | | if (empty($this->oauth['authorize'])) { |
| | | $this->logError('getOAuthUrl', 'OAuth authorize URL not configured'); |
| | | return ''; |
| | | } |
| | |
| | | 'client_id' => $credentials['client_id']??$credentials['application_id']??$credentials['app_id']??'', |
| | | 'redirect_url' => $this->getRedirectUri(), |
| | | 'response_type' => 'code', |
| | | 'scope' => implode(' ', $credentials['scopes']??[]) |
| | | 'scope' => implode(' ', $this->oauthScopes??[]) |
| | | ]; |
| | | $state_key = wp_generate_password(32, false); |
| | | $user_id = $this->userID??0; |
| | |
| | | $params = $this->addOAuthParams($params); |
| | | } |
| | | |
| | | return $credentials['authorize'] . '?' . http_build_query($params); |
| | | return $this->oauth['authorize'] . '?' . http_build_query($params); |
| | | } |
| | | |
| | | protected function getRedirectUri():string |