1
+ <?php
2
+
3
+ /*
4
+ * Copyright 2008 Google Inc.
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */
18
+
19
+ use GuzzleHttp \Client ;
20
+ use GuzzleHttp \ClientInterface ;
21
+ use Google \Auth \CacheInterface ;
22
+
23
+ /**
24
+ * Wrapper around Google Access Tokens which provides convenience functions
25
+ *
26
+ */
27
+ class Google_AccessToken
28
+ {
29
+ const FEDERATED_SIGNON_CERT_URL = 'https://www.googleapis.com/oauth2/v1/certs ' ;
30
+
31
+ /**
32
+ * @var array The access token.
33
+ */
34
+ private $ token ;
35
+
36
+ /**
37
+ * @var GuzzleHttp\ClientInterface The http client
38
+ */
39
+ private $ http ;
40
+
41
+ /**
42
+ * @var Google\Auth\CacheInterface cache class
43
+ */
44
+ private $ cache ;
45
+
46
+ /**
47
+ * Instantiates the class, but does not initiate the login flow, leaving it
48
+ * to the discretion of the caller.
49
+ */
50
+ public function __construct ($ token , ClientInterface $ http = null , CacheInterface $ cache = null )
51
+ {
52
+ if (is_null ($ http )) {
53
+ $ http = new Client ();
54
+ }
55
+
56
+ $ this ->http = $ http ;
57
+ $ this ->cache = $ cache ;
58
+ $ this ->setAccessToken ($ token );
59
+ }
60
+
61
+ /**
62
+ * @param string|array $token
63
+ * @throws InvalidArgumentException
64
+ */
65
+ public function setAccessToken ($ token )
66
+ {
67
+ if (is_string ($ token )) {
68
+ if ($ json = json_decode ($ token , true )) {
69
+ $ token = $ json ;
70
+ } else {
71
+ // assume $token is just the token string
72
+ $ token = [
73
+ 'access_token ' => $ token ,
74
+ ];
75
+ }
76
+ }
77
+ if ($ token == null ) {
78
+ throw new InvalidArgumentException ('invalid json token ' );
79
+ }
80
+ if (!isset ($ token ['access_token ' ])) {
81
+ throw new InvalidArgumentException ("Invalid token format " );
82
+ }
83
+ $ this ->token = $ token ;
84
+ }
85
+
86
+ public function getAccessToken ()
87
+ {
88
+ return $ this ->token ;
89
+ }
90
+
91
+ /**
92
+ * Revoke an OAuth2 access token or refresh token. This method will revoke the current access
93
+ * token, if a token isn't provided.
94
+ * @throws Google_Auth_Exception
95
+ * @param string|null $token The token (access token or a refresh token) that should be revoked.
96
+ * @return boolean Returns True if the revocation was successful, otherwise False.
97
+ */
98
+ public function revokeToken ()
99
+ {
100
+ if (!$ this ->token ) {
101
+ // Not initialized, no token to actually revoke
102
+ return false ;
103
+ } elseif (array_key_exists ('refresh_token ' , $ this ->token )) {
104
+ $ token = $ this ->token ['refresh_token ' ];
105
+ } else {
106
+ $ token = $ this ->token ['access_token ' ];
107
+ }
108
+
109
+ $ request = $ this ->http ->createRequest ('POST ' , Google_Client::OAUTH2_REVOKE_URI );
110
+ $ request ->addHeader ('Cache-Control ' , 'no-store ' );
111
+ $ request ->addHeader ('Content-Type ' , 'application/x-www-form-urlencoded ' );
112
+ $ request ->getBody ()->replaceFields (array ('token ' => $ token ));
113
+
114
+ $ response = $ this ->http ->send ($ request );
115
+ if ($ response ->getStatusCode () == 200 ) {
116
+ $ this ->token = null ;
117
+
118
+ return true ;
119
+ }
120
+
121
+ return false ;
122
+ }
123
+
124
+ /**
125
+ * Retrieve and cache a certificates file.
126
+ *
127
+ * @param $url string location
128
+ * @throws Google_Auth_Exception
129
+ * @return array certificates
130
+ */
131
+ private function retrieveCertsFromLocation ($ url )
132
+ {
133
+ // If we're retrieving a local file, just grab it.
134
+ if ("http " != substr ($ url , 0 , 4 )) {
135
+ $ file = file_get_contents ($ url );
136
+ if ($ file ) {
137
+ return json_decode ($ file , true );
138
+ } else {
139
+ throw new Google_Auth_Exception (
140
+ "Failed to retrieve verification certificates: ' " .
141
+ $ url . "'. "
142
+ );
143
+ }
144
+ }
145
+
146
+ $ response = $ this ->http ->get ($ url );
147
+
148
+ if ($ response ->getStatusCode () == 200 ) {
149
+ return $ response ->json ();
150
+ }
151
+ throw new Google_Auth_Exception (
152
+ "Failed to retrieve verification certificates: ' " .
153
+ $ response ->getBody ()->getContents () . "'. " ,
154
+ $ response ->getStatusCode ()
155
+ );
156
+ }
157
+
158
+ // Gets federated sign-on certificates to use for verifying identity tokens.
159
+ // Returns certs as array structure, where keys are key ids, and values
160
+ // are PEM encoded certificates.
161
+ private function getFederatedSignOnCerts ()
162
+ {
163
+ $ cache = $ this ->getCache ();
164
+
165
+ if (!$ certs = $ cache ->get ('federated_signon_certs ' )) {
166
+ $ certs = $ this ->retrieveCertsFromLocation (
167
+ self ::FEDERATED_SIGNON_CERT_URL
168
+ );
169
+
170
+ $ cache ->set ('federated_signon_certs ' , $ certs );
171
+ }
172
+
173
+ return $ certs ;
174
+ }
175
+
176
+ /**
177
+ * Verifies an id token and returns the authenticated apiLoginTicket.
178
+ * Throws an exception if the id token is not valid.
179
+ * The audience parameter can be used to control which id tokens are
180
+ * accepted. By default, the id token must have been issued to this OAuth2 client.
181
+ *
182
+ * @param $audience
183
+ * @return array the token payload, if successful
184
+ * @throws Google_Auth_Exception
185
+ */
186
+ public function verifyIdToken ($ audience = null )
187
+ {
188
+ if (empty ($ this ->token ['id_token ' ])) {
189
+ throw new LogicException ('id_token cannot be null ' );
190
+ }
191
+
192
+ // Check signature
193
+ $ certs = $ this ->getFederatedSignonCerts ();
194
+ foreach ($ certs as $ keyName => $ pem ) {
195
+ $ key = openssl_x509_read ($ pem );
196
+
197
+ try {
198
+ $ payload = JWT ::decode ($ this ->token ['id_token ' ], $ key , array ('RS256 ' ));
199
+ if (is_null ($ audience ) || (property_exists ($ resp , 'aud ' ) && $ resp ->aud == $ audience )) {
200
+ return $ payload ;
201
+ }
202
+ openssl_x509_free ($ key );
203
+ } catch (ExpiredException $ e ) {
204
+ return false ;
205
+ // continue
206
+ } catch (DomainException $ e ) {
207
+ // continue
208
+ }
209
+ }
210
+
211
+ return false ;
212
+ }
213
+
214
+ public function getCache ()
215
+ {
216
+ if (!$ this ->cache ) {
217
+ $ this ->cache = $ this ->createDefaultCache ();
218
+ }
219
+
220
+ return $ this ->cache ;
221
+ }
222
+
223
+ protected function createDefaultCache ()
224
+ {
225
+ return new Google_Cache_File (sys_get_temp_dir ().'/google-api-php-client ' );
226
+ }
227
+ }
0 commit comments