Subversion Repositories oidplus

Rev

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

Rev Author Line No. Line
635 daniel-mar 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
 
1050 daniel-mar 20
use ViaThinkSoft\OIDplus\OIDplus;
21
use ViaThinkSoft\OIDplus\OIDplusGui;
22
use ViaThinkSoft\OIDplus\OIDplusException;
23
use ViaThinkSoft\OIDplus\OIDplusRA;
24
 
635 daniel-mar 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
 
1050 daniel-mar 30
set_exception_handler(array(OIDplusGui::class, 'html_exception_handler'));
635 daniel-mar 31
 
1200 daniel-mar 32
OIDplus::init(true);
33
 
1050 daniel-mar 34
if (OIDplus::baseConfig()->getValue('DISABLE_PLUGIN_ViaThinkSoft\OIDplus\OIDplusPagePublicLoginGoogle', false)) {
635 daniel-mar 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
if ($_GET['state'] != $_COOKIE['csrf_token_weak']) {
866 daniel-mar 47
        die(_L('Missing or wrong CSRF Token'));
635 daniel-mar 48
}
49
 
1149 daniel-mar 50
$cont = url_post_contents(
51
        "https://oauth2.googleapis.com/token",
52
        array(
53
                "grant_type"    => "authorization_code",
54
                "code"          => $_GET['code'],
55
                "redirect_uri"  => OIDplus::webpath(__DIR__,OIDplus::PATH_ABSOLUTE_CANONICAL).'oauth.php',
56
                "client_id"     => OIDplus::baseConfig()->getValue('GOOGLE_OAUTH2_CLIENT_ID'),
57
                "client_secret" => OIDplus::baseConfig()->getValue('GOOGLE_OAUTH2_CLIENT_SECRET')
58
        )
59
);
60
 
61
if ($cont === false) {
62
        throw new OIDplusException(_L('Communication with %1 server failed', 'Google'));
635 daniel-mar 63
}
64
 
65
// Get ID token and Access token
66
$data = json_decode($cont,true);
67
if (isset($data['error'])) {
68
        throw new OIDplusException(_L('Error receiving the authentication token from %1: %2','Google',$data['error'].' '.$data['error_description']));
69
}
70
$id_token = $data['id_token'];
71
$access_token = $data['access_token'];
72
 
73
try {
74
 
75
        // Decode JWT "id_token"
76
        // see https://medium.com/@darutk/understanding-id-token-5f83f50fa02e
77
        // Note: We do not need to verify the signature because the token comes directly from Google,
78
        //       but we do it anyway. Just to be sure!
1149 daniel-mar 79
        $certs = url_get_contents('https://www.googleapis.com/oauth2/v1/certs');
80
        if ($certs === false) {
81
                throw new OIDplusException(_L('Communication with %1 server failed', 'Google'));
82
        }
83
        $verification_certs = json_decode($certs, true);
635 daniel-mar 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
1149 daniel-mar 103
                        $result = url_post_contents(
104
                                'https://www.googleapis.com/oauth2/v3/userinfo',
105
                                array(
106
                                        "client_id"       => OIDplus::baseConfig()->getValue('GOOGLE_OAUTH2_CLIENT_ID'),
107
                                        "client_secret"   => OIDplus::baseConfig()->getValue('GOOGLE_OAUTH2_CLIENT_SECRET'),
108
                                        "token_type_hint" => "access_token",
109
                                        "token"           => $access_token
110
                                ),
111
                                array(
112
                                        "Authorization"   => "Bearer $access_token"
113
                                )
114
                        );
115
 
116
                        if ($result === false) {
117
                                throw new OIDplusException(_L('Communication with %1 server failed', 'Google'));
118
                        }
119
 
635 daniel-mar 120
                        $data = json_decode($result,true);
121
                        $personal_name = $data['name']; // = given_name + " " + family_name
122
 
123
                        OIDplus::db()->query("update ###ra set ra_name = ?, personal_name = ? where email = ?", array($personal_name, $personal_name, $email));
124
 
1267 daniel-mar 125
                        OIDplus::logger()->log("V2:[INFO]RA(%1)", "RA '%1' was created because of successful Google OAuth2 login", $email);
635 daniel-mar 126
                }
127
 
128
                OIDplus::authUtils()->raLoginEx($email, $remember_me=false, 'Google-OAuth2');
129
 
130
                OIDplus::db()->query("UPDATE ###ra set last_login = ".OIDplus::db()->sqlDate()." where email = ?", array($email));
131
 
1005 daniel-mar 132
                OIDplus::invoke_shutdown();
133
 
635 daniel-mar 134
                // Go back to OIDplus
135
 
801 daniel-mar 136
                header('Location:'.OIDplus::webpath(null,OIDplus::PATH_ABSOLUTE_CANONICAL));
635 daniel-mar 137
        }
138
 
139
} finally {
140
 
141
        // We now have the data of the person that wanted to log in
142
        // So we can log off again
1149 daniel-mar 143
        $cont = url_post_contents(
144
                "https://oauth2.googleapis.com/revoke",
145
                array(
146
                        "client_id"       => OIDplus::baseConfig()->getValue('GOOGLE_OAUTH2_CLIENT_ID'),
147
                        "client_secret"   => OIDplus::baseConfig()->getValue('GOOGLE_OAUTH2_CLIENT_SECRET'),
148
                        "token_type_hint" => "access_token",
149
                        "token"           => urlencode($access_token)
150
                )
635 daniel-mar 151
        );
152
 
1149 daniel-mar 153
        if ($cont === false) {
154
                // throw new OIDplusException(_L('Communication with %1 server failed', 'Google'));
155
        }
156
 
635 daniel-mar 157
}