Subversion Repositories oidplus

Rev

Rev 715 | Rev 866 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. <?php
  2.  
  3. /*
  4.  * OIDplus 2.0
  5.  * Copyright 2019 - 2021 Daniel Marschall, ViaThinkSoft
  6.  *
  7.  * Licensed under the Apache License, Version 2.0 (the "License");
  8.  * you may not use this file except in compliance with the License.
  9.  * You may obtain a copy of the License at
  10.  *
  11.  *     http://www.apache.org/licenses/LICENSE-2.0
  12.  *
  13.  * Unless required by applicable law or agreed to in writing, software
  14.  * distributed under the License is distributed on an "AS IS" BASIS,
  15.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16.  * See the License for the specific language governing permissions and
  17.  * limitations under the License.
  18.  */
  19.  
  20. # More information about the OAuth2 implementation:
  21. # - https://developers.google.com/identity/protocols/oauth2/openid-connect
  22.  
  23. require_once __DIR__ . '/../../../../includes/oidplus.inc.php';
  24.  
  25. OIDplus::init(true);
  26. set_exception_handler(array('OIDplusGui', 'html_exception_handler'));
  27.  
  28. if (OIDplus::baseConfig()->getValue('DISABLE_PLUGIN_OIDplusPagePublicLoginGoogle', false)) {
  29.         throw new OIDplusException(_L('This plugin was disabled by the system administrator!'));
  30. }
  31.  
  32. if (!OIDplus::baseConfig()->getValue('GOOGLE_OAUTH2_ENABLED', false)) {
  33.         throw new OIDplusException(_L('Google OAuth authentication is disabled on this system.'));
  34. }
  35.  
  36. _CheckParamExists($_GET, 'code');
  37. _CheckParamExists($_GET, 'state');
  38. _CheckParamExists($_COOKIE, 'csrf_token_weak');
  39.  
  40. if ($_GET['state'] != $_COOKIE['csrf_token_weak']) {
  41.         die(_L('Wrong CSRF Token'));
  42. }
  43.  
  44. if (!function_exists('curl_init')) {
  45.         die(_L('The "%1" PHP extension is not installed at your system. Please enable the PHP extension <code>%2</code>.','CURL','php_curl'));
  46. }
  47.  
  48. $ch = curl_init();
  49. if (ini_get('curl.cainfo') == '') curl_setopt($ch, CURLOPT_CAINFO, OIDplus::localpath() . 'vendor/cacert.pem');
  50. curl_setopt($ch, CURLOPT_URL,"https://oauth2.googleapis.com/token");
  51. curl_setopt($ch, CURLOPT_USERAGENT, 'ViaThinkSoft-OIDplus/2.0');
  52. curl_setopt($ch, CURLOPT_POST, 1);
  53. curl_setopt($ch, CURLOPT_POSTFIELDS,
  54.         "grant_type=authorization_code&".
  55.         "code=".urlencode($_GET['code'])."&".
  56.         "redirect_uri=".urlencode(OIDplus::webpath(__DIR__,OIDplus::PATH_ABSOLUTE_CANONICAL).'oauth.php')."&".
  57.         "client_id=".urlencode(OIDplus::baseConfig()->getValue('GOOGLE_OAUTH2_CLIENT_ID'))."&".
  58.         "client_secret=".urlencode(OIDplus::baseConfig()->getValue('GOOGLE_OAUTH2_CLIENT_SECRET'))
  59. );
  60. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  61. $cont = curl_exec($ch);
  62. curl_close($ch);
  63.  
  64. // Get ID token and Access token
  65. $data = json_decode($cont,true);
  66. if (isset($data['error'])) {
  67.         throw new OIDplusException(_L('Error receiving the authentication token from %1: %2','Google',$data['error'].' '.$data['error_description']));
  68. }
  69. $id_token = $data['id_token'];
  70. $access_token = $data['access_token'];
  71.  
  72. try {
  73.  
  74.         // Decode JWT "id_token"
  75.         // see https://medium.com/@darutk/understanding-id-token-5f83f50fa02e
  76.         // 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!
  78.         $verification_certs = json_decode(url_get_contents('https://www.googleapis.com/oauth2/v1/certs'), true);
  79.         \Firebase\JWT\JWT::$leeway = 60; // leeway in seconds
  80.         $data = (array) \Firebase\JWT\JWT::decode($id_token, $verification_certs, array('ES256', 'ES384', 'RS256', 'RS384', 'RS512'));
  81.         if (!isset($data['iss']) || ($data['iss'] !== 'https://accounts.google.com')) {
  82.                 throw new OIDplusException(_L('JWT token could not be decoded'));
  83.         }
  84.  
  85.         // Check if the email was verified
  86.         $email = $data['email'];
  87.         if ($data['email_verified'] != 'true') {
  88.                 throw new OIDplusException(_L('The email address %1 was not verified. Please verify it first!',$email));
  89.         }
  90.  
  91.         // Everything's done! Now login and/or create account
  92.         if (!empty($email)) {
  93.                 $ra = new OIDplusRA($email);
  94.                 if (!$ra->existing()) {
  95.                         $ra->register_ra(null); // create a user account without password
  96.  
  97.                         // Query user infos
  98.                         $ch = curl_init('https://www.googleapis.com/oauth2/v3/userinfo'); // Initialise cURL
  99.                         if (ini_get('curl.cainfo') == '') curl_setopt($ch, CURLOPT_CAINFO, OIDplus::localpath() . 'vendor/cacert.pem');
  100.                         $data_string = '';
  101.                         curl_setopt($ch, CURLOPT_HTTPHEADER, array(
  102.                                 'Content-Length: ' . strlen($data_string),
  103.                                 "Authorization: Bearer ".$access_token
  104.                         ));
  105.                         curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);
  106.                         curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  107.                         curl_setopt($ch, CURLOPT_POST, 1);
  108.                         curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
  109.                         $result = curl_exec($ch);
  110.                         curl_close($ch);
  111.                         $data = json_decode($result,true);
  112.                         $personal_name = $data['name']; // = given_name + " " + family_name
  113.  
  114.                         OIDplus::db()->query("update ###ra set ra_name = ?, personal_name = ? where email = ?", array($personal_name, $personal_name, $email));
  115.  
  116.                         OIDplus::logger()->log("[INFO]RA($email)!", "RA '$email' was created because of successful Google OAuth2 login");
  117.                 }
  118.  
  119.                 OIDplus::authUtils()->raLoginEx($email, $remember_me=false, 'Google-OAuth2');
  120.  
  121.                 OIDplus::db()->query("UPDATE ###ra set last_login = ".OIDplus::db()->sqlDate()." where email = ?", array($email));
  122.  
  123.                 // Go back to OIDplus
  124.  
  125.                 header('Location:'.OIDplus::webpath(null,OIDplus::PATH_ABSOLUTE_CANONICAL));
  126.         }
  127.  
  128. } finally {
  129.  
  130.         // We now have the data of the person that wanted to log in
  131.         // So we can log off again
  132.         $ch = curl_init();
  133.         if (ini_get('curl.cainfo') == '') curl_setopt($ch, CURLOPT_CAINFO, OIDplus::localpath() . 'vendor/cacert.pem');
  134.         curl_setopt($ch, CURLOPT_URL,"https://oauth2.googleapis.com/revoke");
  135.         curl_setopt($ch, CURLOPT_USERAGENT, 'ViaThinkSoft-OIDplus/2.0');
  136.         curl_setopt($ch, CURLOPT_POST, 1);
  137.         curl_setopt($ch, CURLOPT_POSTFIELDS,
  138.                 "client_id=".urlencode(OIDplus::baseConfig()->getValue('GOOGLE_OAUTH2_CLIENT_ID'))."&".
  139.                 "client_secret=".urlencode(OIDplus::baseConfig()->getValue('GOOGLE_OAUTH2_CLIENT_SECRET'))."&".
  140.                 "token_type_hint=access_token&".
  141.                 "token=".urlencode($access_token)
  142.         );
  143.         curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  144.         curl_exec($ch);
  145.         curl_close($ch);
  146.  
  147. }
  148.