Rev 565 | Go to most recent revision | Show entire file | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 565 | Rev 566 | ||
---|---|---|---|
Line 70... | Line 70... | ||
70 | 70 | ||
71 | try { |
71 | try { |
72 | 72 | ||
73 | // Decode JWT "id_token" |
73 | // Decode JWT "id_token" |
74 | // see https://medium.com/@darutk/understanding-id-token-5f83f50fa02e |
74 | // see https://medium.com/@darutk/understanding-id-token-5f83f50fa02e |
75 | // and https://github.com/firebase/php-jwt/blob/master/src/JWT.php |
- | |
76 | // Note: We do not need to verify the signature because the token comes directly from Google, |
75 | // Note: We do not need to verify the signature because the token comes directly from Google, |
77 | // but we do it anyway. Just to be sure! |
76 | // but we do it anyway. Just to be sure! |
78 | $verification_certs = json_decode(file_get_contents('https://www.googleapis.com/oauth2/v1/certs'), true); |
77 | $verification_certs = json_decode(file_get_contents('https://www.googleapis.com/oauth2/v1/certs'), true); |
79 | $data = decode_idtoken($id_token, $verification_certs); |
78 | $data = (array) \Firebase\JWT\JWT::decode($id_token, $verification_certs, array('ES256', 'RS256', 'RS384', 'RS512')); |
80 | if (($data === false) || !isset($data['iss']) || ($data['iss'] !== 'https://accounts.google.com')) { |
79 | if (($data === false) || !isset($data['iss']) || ($data['iss'] !== 'https://accounts.google.com')) { |
81 | throw new OIDplusException(_L('JWT token could not be decoded')); |
80 | throw new OIDplusException(_L('JWT token could not be decoded')); |
82 | } |
81 | } |
83 | 82 | ||
84 | // Check if the email was verified |
83 | // Check if the email was verified |
Line 142... | Line 141... | ||
142 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); |
141 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); |
143 | curl_exec($ch); |
142 | curl_exec($ch); |
144 | curl_close($ch); |
143 | curl_close($ch); |
145 | 144 | ||
146 | } |
145 | } |
147 | - | ||
148 | # ---------------------------------- |
- | |
149 | - | ||
150 | function decode_idtoken($id_token, $verification_certs=null) { |
- | |
151 | // Parts taken and simplified from https://github.com/firebase/php-jwt , licensed by BSD-3-clause |
- | |
152 | // Here is a great page for encode and decode tokens for testing: https://jwt.io/ |
- | |
153 | - | ||
154 | $parts = explode('.', $id_token); |
- | |
155 | if (count($parts) === 5) return false; // encrypted JWT not yet supported |
- | |
156 | if (count($parts) !== 3) return false; |
- | |
157 | list($header_base64, $payload_base64, $signature_base64) = $parts; |
- | |
158 | - | ||
159 | $header_ary = json_decode(urlsafeB64Decode($header_base64),true); |
- | |
160 | if ($header_ary['typ'] !== 'JWT') return false; |
- | |
161 | - | ||
162 | if ($verification_certs) { |
- | |
163 | $key = isset($header_ary['kid']) ? $verification_certs[$header_ary['kid']] : $verification_certs; |
- | |
164 | - | ||
165 | $msg = $header_base64.'.'.$payload_base64; |
- | |
166 | $signature = urlsafeB64Decode($signature_base64); |
- | |
167 | - | ||
168 | $jwt_algo = $header_ary['alg']; |
- | |
169 | if ($jwt_algo != 'none') { |
- | |
170 | $php_algo = 'SHA'.substr($jwt_algo,2,3); |
- | |
171 | switch (substr($jwt_algo,0,2)) { |
- | |
172 | case 'ES': |
- | |
173 | // OpenSSL expects an ASN.1 DER sequence for ES256 signatures |
- | |
174 | $signature = signatureToDER($signature); |
- | |
175 | if (!function_exists('openssl_verify')) break; // if OpenSSL is not installed, we just accept the JWT |
- | |
176 | if (!openssl_verify($msg, $signature, $key, $php_algo)) return false; |
- | |
177 | break; |
- | |
178 | case 'RS': |
- | |
179 | if (!function_exists('openssl_verify')) break; // if OpenSSL is not installed, we just accept the JWT |
- | |
180 | if (!openssl_verify($msg, $signature, $key, $php_algo)) return false; |
- | |
181 | break; |
- | |
182 | case 'HS': |
- | |
183 | $hash = @hash_hmac($php_algo, $msg, $key, true); |
- | |
184 | if (!$hash) break; // if the hash algo is not available, we just accept the JWT |
- | |
185 | if (!hash_equals($signature, $hash)) return false; |
- | |
186 | break; |
- | |
187 | case 'PS': |
- | |
188 | file_put_contents($msg_file = tempnam("/tmp", ""), $msg); |
- | |
189 | file_put_contents($sig_file = tempnam("/tmp", ""), $signature); |
- | |
190 | file_put_contents($key_file = tempnam("/tmp", ""), $key); |
- | |
191 | $ec = -1; |
- | |
192 | $out = array(); |
- | |
193 | $cmd = "openssl dgst -".strtolower($php_algo)." -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-1 -verify ".escapeshellarg($key_file)." -signature ".escapeshellarg($sig_file)." ".escapeshellarg($msg_file); |
- | |
194 | $cmd .= (strtoupper(substr(PHP_OS,0,3)) === 'WIN') ? ' 2> NUL' : ' 2> /dev/null'; |
- | |
195 | exec($cmd, $out, $ec); |
- | |
196 | unlink($msg_file); |
- | |
197 | unlink($sig_file); |
- | |
198 | unlink($key_file); |
- | |
199 | if (($ec !== 0) && (count($out) === 0)) break; // If OpenSSL is not found, we just accept the JWT |
- | |
200 | if (($ec !== 0) || (strpos(implode("\n",$out),"Verified OK") === false)) return false; |
- | |
201 | break; |
- | |
202 | default: |
- | |
203 | return false; |
- | |
204 | } |
- | |
205 | } |
- | |
206 | } |
- | |
207 | - | ||
208 | $payload_ary = json_decode(urlsafeB64Decode($payload_base64), true); |
- | |
209 | - | ||
210 | $leeway = 60; // 1 Minute |
- | |
211 | if (isset($payload_ary['nbf']) && (time()+$leeway<$payload_ary['nbf'])) return false; |
- | |
212 | if (isset($payload_ary['exp']) && (time()-$leeway>$payload_ary['exp'])) return false; |
- | |
213 | - | ||
214 | return $payload_ary; |
- | |
215 | } |
- | |
216 | - | ||
217 | function urlsafeB64Decode($input) { |
- | |
218 | // Taken from https://github.com/firebase/php-jwt , licensed by BSD-3-clause |
- | |
219 | $remainder = strlen($input) % 4; |
- | |
220 | if ($remainder) { |
- | |
221 | $padlen = 4 - $remainder; |
- | |
222 | $input .= str_repeat('=', $padlen); |
- | |
223 | } |
- | |
224 | return base64_decode(strtr($input, '-_', '+/')); |
- | |
225 | } |
- | |
226 | - | ||
227 | function signatureToDER($sig) { |
- | |
228 | // Taken from https://github.com/firebase/php-jwt , licensed by BSD-3-clause, modified |
- | |
229 | - | ||
230 | // Separate the signature into r-value and s-value |
- | |
231 | list($r, $s) = str_split($sig, (int) (strlen($sig) / 2)); |
- | |
232 | - | ||
233 | // Trim leading zeros |
- | |
234 | $r = ltrim($r, "\x00"); |
- | |
235 | $s = ltrim($s, "\x00"); |
- | |
236 | - | ||
237 | // Convert r-value and s-value from unsigned big-endian integers to signed two's complement |
- | |
238 | if (ord($r[0]) > 0x7f) $r = "\x00" . $r; |
- | |
239 | if (ord($s[0]) > 0x7f) $s = "\x00" . $s; |
- | |
240 | - | ||
241 | $der_r = chr(0x00/*primitive*/ | 0x02/*INTEGER*/).chr(strlen($r)).$r; |
- | |
242 | $der_s = chr(0x00/*primitive*/ | 0x02/*INTEGER*/).chr(strlen($s)).$s; |
- | |
243 | $der = chr(0x20/*constructed*/ | 0x10/*SEQUENCE*/).chr(strlen($der_r.$der_s)).$der_r.$der_s; |
- | |
244 | return $der; |
- | |
245 | } |
- |