Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
89.19% covered (success)
89.19%
33 / 37
87.50% covered (success)
87.50%
7 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
GoogleClient
89.19% covered (success)
89.19%
33 / 37
87.50% covered (success)
87.50%
7 / 8
14.25
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 __call
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
3
 set_access_token
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 gt_redirect_url
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 authorization_url
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
1
 access_token
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
2
 user
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
 state
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * Google API Client.
4 *
5 * Useful for authenticating the user and other API related operations.
6 *
7 * @package RtCamp\GoogleLogin
8 * @since 1.0.0
9 */
10
11declare(strict_types=1);
12
13namespace RtCamp\GoogleLogin\Utils;
14
15use Exception;
16
17/**
18 * Class GoogleClient
19 *
20 * @package RtCamp\GoogleLogin\Utils
21 */
22class GoogleClient {
23    /**
24     * Authorization URL.
25     *
26     * @var string
27     */
28    const AUTHORIZE_URL = 'https://accounts.google.com/o/oauth2/auth';
29
30    /**
31     * Access Token URL.
32     *
33     * @var string
34     */
35    const TOKEN_URL = 'https://oauth2.googleapis.com/token';
36
37    /**
38     * API base for google.
39     *
40     * @var string
41     */
42    const API_BASE = 'https://www.googleapis.com';
43
44    /**
45     * Client ID.
46     *
47     * @var string
48     */
49    public $client_id;
50
51    /**
52     * Client secret.
53     *
54     * @var string
55     */
56    public $client_secret;
57
58    /**
59     * Redirect URI.
60     *
61     * @var string
62     */
63    public $redirect_uri;
64
65    /**
66     * Access token.
67     *
68     * @var string
69     */
70    private $access_token;
71
72    /**
73     * GoogleClient constructor.
74     *
75     * @param array $config Configuration for client.
76     */
77    public function __construct( array $config ) {
78        $this->client_id     = $config['client_id'] ?? '';
79        $this->client_secret = $config['client_secret'] ?? '';
80        $this->redirect_uri  = $config['redirect_uri'] ?? '';
81    }
82
83    /**
84     * Check if access token is set before calling API methods.
85     *
86     * @param string $name Name of method called.
87     * @param mixed  $args Arguments for method.
88     *
89     * @throws Exception Empty access token.
90     */
91    public function __call( string $name, $args ) {
92        $methods = [
93            'user',
94            'emails',
95        ];
96
97        if ( in_array( $name, $methods, true ) && empty( $this->access_token ) ) {
98            throw new Exception( __( 'Access token must be set to make this API call', 'login-with-google' ) );
99        }
100    }
101
102    /**
103     * Set access token.
104     *
105     * @param string $code Token.
106     *
107     * @return self
108     * @throws \Throwable Exception for fetching access token.
109     */
110    public function set_access_token( string $code ): self {
111        try {
112            $this->access_token = $this->access_token( $code )->access_token;
113
114            return $this;
115        } catch ( \Throwable $e ) {
116            throw $e;
117        }
118    }
119
120    /**
121     * Return redirect url.
122     *
123     * @return string
124     */
125    public function gt_redirect_url(): string {
126        return apply_filters( 'rtcamp.google_redirect_url', $this->redirect_uri );
127    }
128
129    /**
130     * Get the authorize URL
131     *
132     * @return string
133     */
134    public function authorization_url(): string {
135        $plugin_scope = [
136            'email',
137            'profile',
138            'openid',
139        ];
140
141        $scope = apply_filters_deprecated(
142            'wp_google_login_scopes',
143            [
144                $plugin_scope,
145            ],
146            '1.0.15',
147            'rtcamp.google_scope'
148        );
149
150        /**
151         * Filter the scopes.
152         *
153         * @param array $scope List of scopes.
154         */
155        $scope = apply_filters( 'rtcamp.google_scope', $scope );
156
157        $client_args = [
158            'client_id'     => $this->client_id,
159            'redirect_uri'  => $this->gt_redirect_url(),
160            'state'         => $this->state(),
161            'scope'         => implode( ' ', $scope ),
162            'access_type'   => 'online',
163            'response_type' => 'code',
164        ];
165
166        /**
167         * Filter the arguments for sending in query.
168         *
169         * This is useful in cases for example: choosing the correct prompt.
170         *
171         * @param array $client_args List of query arguments to send to Google OAuth.
172         */
173        $client_args = apply_filters( 'rtcamp.google_client_args', $client_args );
174
175        return self::AUTHORIZE_URL . '?' . http_build_query( $client_args );
176    }
177
178    /**
179     * Get the access token.
180     *
181     * @param string $code Response code received during authorization.
182     *
183     * @return \stdClass
184     * @throws Exception For access token errors.
185     */
186    public function access_token( string $code ): \stdClass {
187        $response = wp_remote_post(
188            self::TOKEN_URL,
189            [
190                'headers' => [
191                    'Accept' => 'application/json',
192                ],
193                'body'    => [
194                    'client_id'     => $this->client_id,
195                    'client_secret' => $this->client_secret,
196                    'redirect_uri'  => $this->gt_redirect_url(),
197                    'code'          => $code,
198                    'grant_type'    => 'authorization_code',
199                ],
200            ]
201        );
202
203        if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
204            throw new Exception( __( 'Could not retrieve the access token, please try again.', 'login-with-google' ) );
205        }
206
207        return json_decode( wp_remote_retrieve_body( $response ) );
208    }
209
210    /**
211     * Make an API request.
212     *
213     * @return \stdClass
214     * @throws \Throwable Exception during API.
215     * @throws Exception API Exception.
216     */
217    public function user(): \stdClass {
218        try {
219            //phpcs:disable WordPressVIPMinimum.Functions.RestrictedFunctions.wp_remote_get_wp_remote_get
220            $user = wp_remote_get(
221                trailingslashit( self::API_BASE ) . 'oauth2/v2/userinfo?access_token=' . $this->access_token,
222                [
223                    'headers' => [
224                        'Accept' => 'application/json',
225                    ],
226                ]
227            );
228
229            if ( 200 !== wp_remote_retrieve_response_code( $user ) ) {
230                throw new Exception( __( 'Could not retrieve the user information, please try again.', 'login-with-google' ) );
231            }
232
233            return json_decode( wp_remote_retrieve_body( $user ) );
234
235        } catch ( \Throwable $e ) {
236
237            throw $e;
238        }
239    }
240
241    /**
242     * State to pass in GH API.
243     *
244     * @return string
245     */
246    public function state(): string {
247        $state_data['nonce']    = wp_create_nonce( 'login_with_google' );
248        $state_data             = apply_filters( 'rtcamp.google_login_state', $state_data );
249        $state_data['provider'] = 'google';
250
251        return base64_encode( wp_json_encode( $state_data ) );
252    }
253
254}