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