Subversion Repositories oidplus

Rev

Rev 1413 | 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. set_exception_handler(array(OIDplusGui::class, 'html_exception_handler'));
  31.  
  32. OIDplus::init(true);
  33.  
  34. if (OIDplus::baseConfig()->getValue('DISABLE_PLUGIN_ViaThinkSoft\OIDplus\OIDplusPagePublicLoginGoogle', false)) {
  35.         throw new OIDplusException(_L('This plugin was disabled by the system administrator!'));
  36. }
  37.  
  38. if (!OIDplus::baseConfig()->getValue('GOOGLE_OAUTH2_ENABLED', false)) {
  39.         throw new OIDplusException(_L('Google OAuth authentication is disabled on this system.'));
  40. }
  41.  
  42. _CheckParamExists($_GET, 'code');
  43. _CheckParamExists($_GET, 'state');
  44. _CheckParamExists($_COOKIE, 'csrf_token_weak');
  45.  
  46. $ary = explode('|', $_GET['state'], 2);
  47. if (count($ary) !== 2) {
  48.         die(_L('Invalid State'));
  49. }
  50. $redirect_uri = $ary[0] ?? ''; // Attention: Comes from the client. The OAuth2 server MUST verify it! (Google and Facebook does this)
  51. $check_csrf = $ary[1] ?? '';
  52.  
  53. if ($check_csrf != $_COOKIE['csrf_token_weak']) {
  54.         die(_L('Missing or wrong CSRF Token'));
  55. }
  56.  
  57. $cont = url_post_contents(
  58.         "https://oauth2.googleapis.com/token",
  59.         array(
  60.                 "grant_type"    => "authorization_code",
  61.                 "code"          => $_GET['code'],
  62.                 "redirect_uri"  => $redirect_uri,
  63.                 "client_id"     => OIDplus::baseConfig()->getValue('GOOGLE_OAUTH2_CLIENT_ID'),
  64.                 "client_secret" => OIDplus::baseConfig()->getValue('GOOGLE_OAUTH2_CLIENT_SECRET')
  65.         )
  66. );
  67.  
  68. if ($cont === false) {
  69.         throw new OIDplusException(_L('Communication with %1 server failed', 'Google'));
  70. }
  71.  
  72. // Get ID token and Access token
  73. $data = json_decode($cont,true);
  74. if (isset($data['error'])) {
  75.         throw new OIDplusException(_L('Error receiving the authentication token from %1: %2','Google',$data['error'].' '.$data['error_description']));
  76. }
  77. $id_token = $data['id_token'];
  78. $access_token = $data['access_token'];
  79.  
  80. try {
  81.  
  82.         // Decode JWT "id_token"
  83.         // see https://medium.com/@darutk/understanding-id-token-5f83f50fa02e
  84.         // Note: We do not need to verify the signature because the token comes directly from Google,
  85.         //       but we do it anyway. Just to be sure!
  86.         $certs = url_get_contents('https://www.googleapis.com/oauth2/v1/certs');
  87.         if ($certs === false) {
  88.                 throw new OIDplusException(_L('Communication with %1 server failed', 'Google'));
  89.         }
  90.         $verification_certs = json_decode($certs, true);
  91.         \Firebase\JWT\JWT::$leeway = 60; // leeway in seconds
  92.         $cls_data = \Firebase\JWT\JWT::decode($id_token, $verification_certs, array('ES256', 'ES384', 'RS256', 'RS384', 'RS512'));
  93.         $data = json_decode(json_encode($cls_data), true); // convert stdClass to array
  94.         if (!isset($data['iss']) || ($data['iss'] !== 'https://accounts.google.com')) {
  95.                 throw new OIDplusException(_L('JWT token could not be decoded'));
  96.         }
  97.  
  98.         // Check if the email was verified
  99.         $email = $data['email'];
  100.         if ($data['email_verified'] != 'true') {
  101.                 throw new OIDplusException(_L('The email address %1 was not verified. Please verify it first!',$email));
  102.         }
  103.  
  104.         // Everything's done! Now login and/or create account
  105.         if (!empty($email)) {
  106.                 $ra = new OIDplusRA($email);
  107.                 if (!$ra->existing()) {
  108.                         $ra->register_ra(null); // create a user account without password
  109.  
  110.                         // Query user infos
  111.                         $result = url_post_contents(
  112.                                 'https://www.googleapis.com/oauth2/v3/userinfo',
  113.                                 array(
  114.                                         "client_id"       => OIDplus::baseConfig()->getValue('GOOGLE_OAUTH2_CLIENT_ID'),
  115.                                         "client_secret"   => OIDplus::baseConfig()->getValue('GOOGLE_OAUTH2_CLIENT_SECRET'),
  116.                                         "token_type_hint" => "access_token",
  117.                                         "token"           => $access_token
  118.                                 ),
  119.                                 array(
  120.                                         "Authorization"   => "Bearer $access_token"
  121.                                 )
  122.                         );
  123.  
  124.                         if ($result === false) {
  125.                                 throw new OIDplusException(_L('Communication with %1 server failed', 'Google'));
  126.                         }
  127.  
  128.                         $data = json_decode($result,true);
  129.                         $personal_name = $data['name']; // = given_name + " " + family_name
  130.  
  131.                         OIDplus::db()->query("update ###ra set ra_name = ?, personal_name = ? where email = ?", array($personal_name, $personal_name, $email));
  132.  
  133.                         OIDplus::logger()->log("V2:[INFO]RA(%1)", "RA '%1' was created because of successful Google OAuth2 login", $email);
  134.                 }
  135.  
  136.                 OIDplus::authUtils()->raLoginEx($email, 'Google-OAuth2');
  137.  
  138.                 OIDplus::db()->query("UPDATE ###ra set last_login = ".OIDplus::db()->sqlDate()." where email = ?", array($email));
  139.  
  140.                 // Go back to OIDplus
  141.  
  142.                 header('Location:'.OIDplus::webpath(null,OIDplus::PATH_RELATIVE));
  143.         }
  144.  
  145. } finally {
  146.  
  147.         // We now have the data of the person that wanted to log in
  148.         // So we can log off again
  149.         $cont = url_post_contents(
  150.                 "https://oauth2.googleapis.com/revoke",
  151.                 array(
  152.                         "client_id"       => OIDplus::baseConfig()->getValue('GOOGLE_OAUTH2_CLIENT_ID'),
  153.                         "client_secret"   => OIDplus::baseConfig()->getValue('GOOGLE_OAUTH2_CLIENT_SECRET'),
  154.                         "token_type_hint" => "access_token",
  155.                         "token"           => urlencode($access_token)
  156.                 )
  157.         );
  158.  
  159.         if ($cont === false) {
  160.                 // throw new OIDplusException(_L('Communication with %1 server failed', 'Google'));
  161.         }
  162.  
  163.         OIDplus::invoke_shutdown();
  164.  
  165. }
  166.