Subversion Repositories oidplus

Rev

Rev 1005 | Rev 1149 | 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. use ViaThinkSoft\OIDplus\OIDplus;
  21. use ViaThinkSoft\OIDplus\OIDplusGui;
  22. use ViaThinkSoft\OIDplus\OIDplusException;
  23. use ViaThinkSoft\OIDplus\OIDplusRA;
  24.  
  25. # More information about the OAuth2 implementation:
  26. # - https://developers.google.com/identity/protocols/oauth2/openid-connect
  27.  
  28. require_once __DIR__ . '/../../../../includes/oidplus.inc.php';
  29.  
  30. OIDplus::init(true);
  31. set_exception_handler(array(OIDplusGui::class, 'html_exception_handler'));
  32.  
  33. if (OIDplus::baseConfig()->getValue('DISABLE_PLUGIN_ViaThinkSoft\OIDplus\OIDplusPagePublicLoginGoogle', false)) {
  34.         throw new OIDplusException(_L('This plugin was disabled by the system administrator!'));
  35. }
  36.  
  37. if (!OIDplus::baseConfig()->getValue('GOOGLE_OAUTH2_ENABLED', false)) {
  38.         throw new OIDplusException(_L('Google OAuth authentication is disabled on this system.'));
  39. }
  40.  
  41. _CheckParamExists($_GET, 'code');
  42. _CheckParamExists($_GET, 'state');
  43. _CheckParamExists($_COOKIE, 'csrf_token_weak');
  44.  
  45. if ($_GET['state'] != $_COOKIE['csrf_token_weak']) {
  46.         die(_L('Missing or wrong CSRF Token'));
  47. }
  48.  
  49. if (!function_exists('curl_init')) {
  50.         die(_L('The "%1" PHP extension is not installed at your system. Please enable the PHP extension <code>%2</code>.','CURL','php_curl'));
  51. }
  52.  
  53. $ch = curl_init();
  54. if (ini_get('curl.cainfo') == '') curl_setopt($ch, CURLOPT_CAINFO, OIDplus::localpath() . 'vendor/cacert.pem');
  55. curl_setopt($ch, CURLOPT_URL,"https://oauth2.googleapis.com/token");
  56. curl_setopt($ch, CURLOPT_USERAGENT, 'ViaThinkSoft-OIDplus/2.0');
  57. curl_setopt($ch, CURLOPT_POST, 1);
  58. curl_setopt($ch, CURLOPT_POSTFIELDS,
  59.         "grant_type=authorization_code&".
  60.         "code=".urlencode($_GET['code'])."&".
  61.         "redirect_uri=".urlencode(OIDplus::webpath(__DIR__,OIDplus::PATH_ABSOLUTE_CANONICAL).'oauth.php')."&".
  62.         "client_id=".urlencode(OIDplus::baseConfig()->getValue('GOOGLE_OAUTH2_CLIENT_ID'))."&".
  63.         "client_secret=".urlencode(OIDplus::baseConfig()->getValue('GOOGLE_OAUTH2_CLIENT_SECRET'))
  64. );
  65. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  66. $cont = curl_exec($ch);
  67. curl_close($ch);
  68.  
  69. // Get ID token and Access token
  70. $data = json_decode($cont,true);
  71. if (isset($data['error'])) {
  72.         throw new OIDplusException(_L('Error receiving the authentication token from %1: %2','Google',$data['error'].' '.$data['error_description']));
  73. }
  74. $id_token = $data['id_token'];
  75. $access_token = $data['access_token'];
  76.  
  77. try {
  78.  
  79.         // Decode JWT "id_token"
  80.         // see https://medium.com/@darutk/understanding-id-token-5f83f50fa02e
  81.         // Note: We do not need to verify the signature because the token comes directly from Google,
  82.         //       but we do it anyway. Just to be sure!
  83.         $verification_certs = json_decode(url_get_contents('https://www.googleapis.com/oauth2/v1/certs'), true);
  84.         \Firebase\JWT\JWT::$leeway = 60; // leeway in seconds
  85.         $data = (array) \Firebase\JWT\JWT::decode($id_token, $verification_certs, array('ES256', 'ES384', 'RS256', 'RS384', 'RS512'));
  86.         if (!isset($data['iss']) || ($data['iss'] !== 'https://accounts.google.com')) {
  87.                 throw new OIDplusException(_L('JWT token could not be decoded'));
  88.         }
  89.  
  90.         // Check if the email was verified
  91.         $email = $data['email'];
  92.         if ($data['email_verified'] != 'true') {
  93.                 throw new OIDplusException(_L('The email address %1 was not verified. Please verify it first!',$email));
  94.         }
  95.  
  96.         // Everything's done! Now login and/or create account
  97.         if (!empty($email)) {
  98.                 $ra = new OIDplusRA($email);
  99.                 if (!$ra->existing()) {
  100.                         $ra->register_ra(null); // create a user account without password
  101.  
  102.                         // Query user infos
  103.                         $ch = curl_init('https://www.googleapis.com/oauth2/v3/userinfo'); // Initialise cURL
  104.                         if (ini_get('curl.cainfo') == '') curl_setopt($ch, CURLOPT_CAINFO, OIDplus::localpath() . 'vendor/cacert.pem');
  105.                         $data_string = '';
  106.                         curl_setopt($ch, CURLOPT_HTTPHEADER, array(
  107.                                 'Content-Length: ' . strlen($data_string),
  108.                                 "Authorization: Bearer ".$access_token
  109.                         ));
  110.                         curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);
  111.                         curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  112.                         curl_setopt($ch, CURLOPT_POST, 1);
  113.                         curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
  114.                         $result = curl_exec($ch);
  115.                         curl_close($ch);
  116.                         $data = json_decode($result,true);
  117.                         $personal_name = $data['name']; // = given_name + " " + family_name
  118.  
  119.                         OIDplus::db()->query("update ###ra set ra_name = ?, personal_name = ? where email = ?", array($personal_name, $personal_name, $email));
  120.  
  121.                         OIDplus::logger()->log("[INFO]RA($email)!", "RA '$email' was created because of successful Google OAuth2 login");
  122.                 }
  123.  
  124.                 OIDplus::authUtils()->raLoginEx($email, $remember_me=false, 'Google-OAuth2');
  125.  
  126.                 OIDplus::db()->query("UPDATE ###ra set last_login = ".OIDplus::db()->sqlDate()." where email = ?", array($email));
  127.  
  128.                 OIDplus::invoke_shutdown();
  129.  
  130.                 // Go back to OIDplus
  131.  
  132.                 header('Location:'.OIDplus::webpath(null,OIDplus::PATH_ABSOLUTE_CANONICAL));
  133.         }
  134.  
  135. } finally {
  136.  
  137.         // We now have the data of the person that wanted to log in
  138.         // So we can log off again
  139.         $ch = curl_init();
  140.         if (ini_get('curl.cainfo') == '') curl_setopt($ch, CURLOPT_CAINFO, OIDplus::localpath() . 'vendor/cacert.pem');
  141.         curl_setopt($ch, CURLOPT_URL,"https://oauth2.googleapis.com/revoke");
  142.         curl_setopt($ch, CURLOPT_USERAGENT, 'ViaThinkSoft-OIDplus/2.0');
  143.         curl_setopt($ch, CURLOPT_POST, 1);
  144.         curl_setopt($ch, CURLOPT_POSTFIELDS,
  145.                 "client_id=".urlencode(OIDplus::baseConfig()->getValue('GOOGLE_OAUTH2_CLIENT_ID'))."&".
  146.                 "client_secret=".urlencode(OIDplus::baseConfig()->getValue('GOOGLE_OAUTH2_CLIENT_SECRET'))."&".
  147.                 "token_type_hint=access_token&".
  148.                 "token=".urlencode($access_token)
  149.         );
  150.         curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  151.         curl_exec($ch);
  152.         curl_close($ch);
  153.  
  154. }
  155.