PluginProbe
Timetics – Appointment Booking Calendar & Scheduling / trunk
Timetics – Appointment Booking Calendar & Scheduling vtrunk
1.0.61 1.0.60 1.0.59 1.0.58 1.0.57 1.0.56 trunk 1.0.0 1.0.1 1.0.10 1.0.11 1.0.12 1.0.13 1.0.14 1.0.15 1.0.16 1.0.17 1.0.18 1.0.19 1.0.2 1.0.20 1.0.21 1.0.22 1.0.23 1.0.24 All 62 releases
timetics / core / integrations / google / client.php

client.php in Timetics – Appointment Booking Calendar & Scheduling trunk, at core/integrations/google/client.php

279 lines 7.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Google Calendar Auth Class
4 *
5 * @package Timetics
6 */
7 namespace Timetics\Core\Integrations\Google;
8
9 defined( 'ABSPATH' ) || exit;
10
11 use Exception;
12 use InvalidArgumentException;
13
14 /**
15 * Class Auth
16 */
17 class Client {
18 const TIMETICS_REVOKE_URI = 'https://oauth2.googleapis.com/revoke';
19 const TIMETICS_TOKEN_URI = 'https://oauth2.googleapis.com/token';
20 const TIMETICS_AUTH_URL = 'https://accounts.google.com/o/oauth2/v2/auth';
21 const API_BASE_PATH = 'https://www.googleapis.com';
22
23 /**
24 * Store google app client id
25 *
26 * @var string
27 */
28 private $client_id;
29
30 /**
31 * Store google app secrete
32 *
33 * @var string
34 */
35 private $client_secrete;
36
37 /**
38 * Store google app auth scope
39 *
40 * @var string
41 */
42 private $auth_scope;
43
44 /**
45 * Store google auth redirect uri
46 *
47 * @var string
48 */
49 private $redirect_uri;
50
51 /**
52 * Get app client id
53 *
54 * @return string
55 */
56 public function get_client_id() {
57 return $this->client_id;
58 }
59
60 /**
61 * Get app secrete
62 *
63 * @return string
64 */
65 public function get_client_secrete() {
66 return $this->client_secrete;
67 }
68
69 /**
70 * Get app auth scope
71 *
72 * @return string
73 */
74 public function get_auth_scope() {
75 return $this->auth_scope;
76 }
77
78 /**
79 * Get auth redirect uri
80 *
81 * @return string
82 */
83 public function get_redirect_uri() {
84 return $this->redirect_uri;
85 }
86
87 /**
88 * Get auth url
89 *
90 * @return string
91 */
92 public function get_auth_url( $state = '' ) {
93 $params = array(
94 'client_id' => $this->client_id,
95 'scope' => urlencode_deep( $this->auth_scope ),
96 'redirect_uri' => $this->redirect_uri,
97 'response_type' => 'code',
98 'access_type' => 'offline',
99 'prompt' => 'consent',
100 );
101
102 if ( ! empty( $state ) ) {
103 $params['state'] = $state;
104 }
105
106 return add_query_arg( $params, self::TIMETICS_AUTH_URL );
107 }
108
109 /**
110 * Set google authentication configuration
111 *
112 * @return void
113 */
114 public function set_auth_config( $args = array() ) {
115 $defaults = array(
116 'client_id' => '',
117 'client_secrete' => '',
118 );
119
120 $args = wp_parse_args( $args, $defaults );
121
122 $this->client_id = $args['client_id'];
123 $this->client_secrete = $args['client_secrete'];
124 }
125
126 /**
127 * Set authentication scope
128 *
129 * @return void
130 */
131 public function add_scope( $scope ) {
132 $this->auth_scope = $scope;
133 }
134
135 /**
136 * Set redirect uri
137 *
138 * @param string $uri Redirect uri
139 *
140 * @return void
141 */
142 public function set_redirect_uri( $uri ) {
143 $this->redirect_uri = $uri;
144 }
145
146 /**
147 * Fetch access token
148 *
149 * @param string $code
150 *
151 * @return array Token data on success.
152 * @throws \InvalidArgumentException When the code is empty.
153 * @throws \Exception On transport failure or a non-200 token response.
154 */
155 public function fetch_access_token_with_auth_code( $code ) {
156 if ( strlen( $code ) === 0 ) {
157 throw new InvalidArgumentException( 'Invalid code' );
158 }
159
160 $args = array(
161 'client_id' => $this->client_id,
162 'client_secret' => $this->client_secrete,
163 'code' => $code,
164 'redirect_uri' => $this->redirect_uri,
165 'grant_type' => 'authorization_code',
166 );
167
168 $response = wp_remote_post( self::TIMETICS_TOKEN_URI, array( 'body' => $args ) );
169
170 if ( is_wp_error( $response ) ) {
171 throw new Exception( esc_html( $response->get_error_message() ) );
172 }
173
174 $status_code = (int) wp_remote_retrieve_response_code( $response );
175 $body = wp_remote_retrieve_body( $response );
176 $data = json_decode( $body, true );
177
178 if ( 200 !== $status_code || empty( $data['access_token'] ) ) {
179 $message = ! empty( $data['error_description'] ) ? $data['error_description'] : __( 'Failed to obtain Google access token.', 'timetics' );
180 throw new Exception( esc_html( $message ) );
181 }
182
183 return $data;
184 }
185
186 /**
187 * Fetch access token by using refresh token
188 *
189 * @param string $refresh_token
190 *
191 * @return array|\WP_Error Token data on success, or a WP_Error whose error_data carries a boolean `transient` flag.
192 */
193 public function fetch_access_token_with_refresh_token( $refresh_token ) {
194 if ( empty( $refresh_token ) ) {
195 return new \WP_Error(
196 'timetics_google_no_refresh_token',
197 'No Google refresh token is available for this account.',
198 array( 'transient' => false )
199 );
200 }
201
202 $args = array(
203 'client_id' => $this->client_id,
204 'client_secret' => $this->client_secrete,
205 'refresh_token' => $refresh_token,
206 'redirect_uri' => $this->redirect_uri,
207 'grant_type' => 'refresh_token',
208 );
209
210 $response = wp_remote_post( self::TIMETICS_TOKEN_URI, array( 'body' => $args ) );
211
212 // Transport-level failure (DNS, timeout, connection refused). Transient:
213 // the credential is fine, Google was just briefly unreachable.
214 if ( is_wp_error( $response ) ) {
215 return new \WP_Error(
216 'timetics_google_refresh_http_error',
217 $response->get_error_message(),
218 array( 'transient' => true )
219 );
220 }
221
222 $status_code = (int) wp_remote_retrieve_response_code( $response );
223 $body = wp_remote_retrieve_body( $response );
224 $data = json_decode( $body, true );
225
226 if ( 200 === $status_code && ! empty( $data['access_token'] ) ) {
227 return $data;
228 }
229
230 $google_error = isset( $data['error'] ) ? $data['error'] : '';
231 $transient = ( 'invalid_grant' !== $google_error );
232
233 if ( 429 === $status_code || $status_code >= 500 || 0 === $status_code ) {
234 $transient = true;
235 }
236
237 if ( defined( 'WP_DEBUG' ) && WP_DEBUG && defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG ) {
238 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Debug logging is guarded by WP_DEBUG checks.
239 error_log( sprintf( 'Timetics Google token refresh failed (HTTP %d): %s', $status_code, $body ) );
240 }
241
242 return new \WP_Error(
243 'timetics_google_refresh_failed',
244 ! empty( $data['error_description'] ) ? $data['error_description'] : 'Failed to refresh Google access token.',
245 [
246 'transient' => $transient,
247 'status_code' => $status_code,
248 'error' => $google_error,
249 ]
250 );
251 }
252
253 /**
254 * Revoke authorization
255 *
256 * @param string $token
257 *
258 * @return bool
259 */
260 public function revoke( $token ) {
261 $response = wp_remote_post(
262 self::TIMETICS_REVOKE_URI, array(
263 'headers' => array(
264 'content-type' => 'application/x-www-form-urlencoded',
265 ),
266 'body' => build_query(
267 array(
268 'token' => $token,
269 )
270 ),
271 )
272 );
273
274 $status_code = wp_remote_retrieve_response_code( $response );
275
276 return $status_code === 200;
277 }
278 }
279