Subversion Repositories oidplus

Rev

Rev 566 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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