Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
87.50% covered (success)
87.50%
7 / 8
CRAP
88.33% covered (success)
88.33%
53 / 60
GoogleClient
0.00% covered (danger)
0.00%
0 / 1
87.50% covered (success)
87.50%
7 / 8
14.13
91.38% covered (success)
91.38%
53 / 58
 __construct
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
5 / 5
 __call
100.00% covered (success)
100.00%
1 / 1
3
100.00% covered (success)
100.00%
5 / 5
 set_access_token
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
5 / 5
 gt_redirect_url
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
2 / 2
 authorization_url
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
15 / 15
 access_token
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
12 / 12
 user
100.00% covered (success)
100.00%
1 / 1
3
100.00% covered (success)
100.00%
9 / 9
 state
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 5
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        $scope = apply_filters( 'rtcamp.google_scope', $scope );
151
152        return self::AUTHORIZE_URL . '?' . http_build_query(
153            [
154                'client_id'     => $this->client_id,
155                'redirect_uri'  => $this->gt_redirect_url(),
156                'state'         => $this->state(),
157                'scope'         => implode( ' ', $scope ),
158                'access_type'   => 'online',
159                'response_type' => 'code',
160            ]
161        );
162    }
163
164    /**
165     * Get the access token.
166     *
167     * @param string $code Response code received during authorization.
168     *
169     * @return \stdClass
170     * @throws Exception For access token errors.
171     */
172    public function access_token( string $code ): \stdClass {
173        $response = wp_remote_post(
174            self::TOKEN_URL,
175            [
176                'headers' => [
177                    'Accept' => 'application/json',
178                ],
179                'body'    => [
180                    'client_id'     => $this->client_id,
181                    'client_secret' => $this->client_secret,
182                    'redirect_uri'  => $this->gt_redirect_url(),
183                    'code'          => $code,
184                    'grant_type'    => 'authorization_code',
185                ],
186            ]
187        );
188
189        if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
190            throw new Exception( __( 'Could not retrieve the access token, please try again.', 'login-with-google' ) );
191        }
192
193        return json_decode( wp_remote_retrieve_body( $response ) );
194    }
195
196    /**
197     * Make an API request.
198     *
199     * @return \stdClass
200     * @throws \Throwable Exception during API.
201     * @throws Exception API Exception.
202     */
203    public function user(): \stdClass {
204        try {
205            //phpcs:disable WordPressVIPMinimum.Functions.RestrictedFunctions.wp_remote_get_wp_remote_get
206            $user = wp_remote_get(
207                trailingslashit( self::API_BASE ) . 'oauth2/v2/userinfo?access_token=' . $this->access_token,
208                [
209                    'headers' => [
210                        'Accept' => 'application/json',
211                    ],
212                ]
213            );
214
215            if ( 200 !== wp_remote_retrieve_response_code( $user ) ) {
216                throw new Exception( __( 'Could not retrieve the user information, please try again.', 'login-with-google' ) );
217            }
218
219            return json_decode( wp_remote_retrieve_body( $user ) );
220
221        } catch ( \Throwable $e ) {
222
223            throw $e;
224        }
225    }
226
227    /**
228     * State to pass in GH API.
229     *
230     * @return string
231     */
232    public function state(): string {
233        $state_data['nonce']    = wp_create_nonce( 'login_with_google' );
234        $state_data             = apply_filters( 'rtcamp.google_login_state', $state_data );
235        $state_data['provider'] = 'google';
236
237        return base64_encode( wp_json_encode( $state_data ) );
238    }
239
240}