Subversion Repositories oidplus

Rev

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