Completed
Push — master ( c0f7fd...0a9f53 )
by Terrence
14:09
created

index-functions.php ➔ getORCIDAMR()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
nc 5
nop 1
dl 0
loc 20
rs 9.6
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * This file contains functions called by index.php. The index.php
5
 * file should include this file with the following statement at the top:
6
 *
7
 * require_once __DIR__ . '/index-functions.php';
8
 */
9
10
use CILogon\Service\Util;
11
use CILogon\Service\OAuth2Provider;
12
use League\OAuth2\Client\Token\AccessToken;
13
use Lcobucci\JWT\Parser;
14
15
/**
16
 * getUserAndRespond
17
 *
18
 * This function specifically handles the redirect from OAuth2 providers.
19
 * The function reads client keys/secrets from a local configuration file,
20
 * and then uses the PHP League OAuth2 client library to extract user
21
 * parameters from the HTTP response.
22
 */
23
function getUserAndRespond()
24
{
25
    $providerId = '';
26
    $providerName = '';
27
    $first_name = '';
28
    $last_name = '';
29
    $display_name = '';
30
    $email = '';
31
    $open_id = '';
32
    $oidc = '';
33
    $amr = '';
34
35
    Util::unsetSessionVar('logonerror');
36
37
    $state = Util::getGetVar('state');  // 'state' must match last CSRF value
38
    $code = Util::getGetVar('code');    // 'code' must not be empty
39
    $lastcsrf = Util::getCsrf()->getTheCookie();
40
    if ($state != $lastcsrf) {
41
        // Verify that response's 'state' equals the last CSRF token
42
        Util::setSessionVar('logonerror', 'Invalid state parameter.');
43
    } elseif (strlen($code) == 0) {
44
        // Make sure the response has a non-empty 'code'
45
        $error = Util::getGetVar('error');
46
        $error_description = Util::getGetVar('error_description');
47
        if ((strlen($error) > 0) && (strlen($error_description) > 0)) {
48
            Util::setSessionVar('logonerror', $error_description . '. Please try again.');
49
        } else {
50
            Util::setSessionVar('logonerror', 'Empty code parameter. Please try again.');
51
        }
52
    } else {
53
        // When using OAuth or OIDC, check portalcookie for providerId
54
        $providerId = Util::getPortalOrCookieVar('providerId');
55
        $providerName = Util::getAuthzIdP($providerId);
56
        $prov = strtolower($providerName); // IdP name all lowercase
57
58
        // Get the client id/secret for the OAuth2 IdP
59
        $clientid     = constant(strtoupper($prov) . '_OAUTH2_CLIENT_ID');
60
        $clientsecret = constant(strtoupper($prov) . '_OAUTH2_CLIENT_SECRET');
61
        if ((strlen($clientid) > 0) && (strlen($clientsecret) > 0)) {
62
            $oauth2 = new OAuth2Provider($providerName);
63
            try {
64
                $token = $oauth2->provider->getAccessToken(
65
                    'authorization_code',
66
                    [ 'code' => $code ]
67
                );
68
                $user = $oauth2->provider->getResourceOwner($token);
69
                $oidc = $user->getId();
70
                $email = $user->getEmail();
71
                // GitHub email may require special handling
72
                if ((strlen($email) == 0) && ($prov == 'github')) {
73
                    $email = getGitHubEmail($oauth2, $token);
74
                }
75
                $display_name = $user->getName();
76
                if ($prov != 'github') { // No first/last for GitHub
77
                    $first_name = $user->getFirstName();
78
                    $last_name = $user->getLastName();
79
                }
80
                // CIL-799 Get the 'amr' claim from the ORCID id_token
81
                if ($prov == 'orcid') {
82
                    $amr = getORCIDAMR($token);
83
                }
84
85
                // CIL-793 - Calculate missing first/last name for OAuth1
86
                $callbackuri = Util::getSessionVar('callbackuri'); // OAuth 1.0a
87
                if (
88
                    (strlen($callbackuri) > 0) &&
89
                    ((strlen($first_name) == 0) ||
90
                     (strlen($last_name) == 0))
91
                ) {
92
                    list($first, $last) = Util::getFirstAndLastName(
0 ignored issues
show
Bug introduced by
The method getFirstAndLastName() does not seem to exist on object<CILogon\Service\Util>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
93
                        $display_name,
94
                        $first_name,
95
                        $last_name
96
                    );
97
                    $first_name = $first;
98
                    $last_name = $last;
99
                }
100
            } catch (Exception $e) {
101
                Util::setSessionVar('logonerror', $e->getMessage());
102
            }
103
        } else {
104
            Util::setSessionVar(
105
                'logonerror',
106
                'Missing OAuth2 client configuration values.'
107
            );
108
        }
109
    }
110
111
    // If no error reported, check for session var 'storeattributes'
112
    // which indicates to simply store the user attributes in the
113
    // PHP session. If not set, then by default save the user
114
    // attributes to the database (which also stores the user
115
    // attributes in the PHP session).
116
    if (strlen(Util::getSessionVar('logonerror')) == 0) {
117
        $func = 'CILogon\Service\Util::saveUserToDataStore';
118
        if (!empty(Util::getSessionVar('storeattributes'))) {
119
            $func = 'CILogon\Service\Util::setUserAttributeSessionVars';
120
        }
121
        $func(
122
            $open_id,
123
            $providerId,
124
            $providerName,
125
            $first_name,
126
            $last_name,
127
            $display_name,
128
            $email,
129
            'openid',
130
            '', // ePPN
131
            '', // ePTID
132
            $open_id,
133
            $oidc,
134
            '', // subject_id
135
            '', // pairwise_id
136
            '', // affiliation
137
            '', // ou
138
            '', // member_of
139
            '', // acr
140
            $amr
141
        );
142
    } else {
143
        Util::unsetSessionVar('submit');
144
    }
145
}
146
147
/**
148
 * getGitHubEmail
149
 *
150
 * This function gets a GitHub user email address from the special
151
 * user email API endpoint. It returns an email address that is marked
152
 * as 'primary' by GitHub.
153
 *
154
 * @param OAuth2Provider An exsiting OAuth2Provider object
155
 * @param League\OAuth2\Client\Token\AccessToken An oauth2 token
156
 * @return string A GitHub user's primary email address, or empty string
157
 *         if no such email address exists.
158
 */
159
function getGitHubEmail($oauth2, $token)
160
{
161
    $oauth2_email = '';
162
163
    $request = $oauth2->provider->getAuthenticatedRequest(
164
        'GET',
165
        'https://api.github.com/user/emails',
166
        $token
167
    );
168
    $github_emails = json_decode(
169
        $oauth2->provider->getResponse($request)->getBody()
170
    );
171
172
    foreach ($github_emails as $email) {
173
        if ($email->primary == 1) {
174
            $oauth2_email = $email->email;
175
            break;
176
        }
177
    }
178
179
    return $oauth2_email;
180
}
181
182
/**
183
 * getORCIDAMR
184
 *
185
 * CIL-799
186
 * Return the 'amr' claim (Authentication Method Reference) from an
187
 * ORCID id_token using the method suggested at 
188
 * https://github.com/thephpleague/oauth2-google#accessing-token-jwt .
189
 */
190
function getORCIDAMR($token)
191
{
192
    $amr = '';
193
194
    $values = @$token->getValues();
195
    if (
196
        (isset($values)) &&
197
        (array_key_exists('id_token', $values))
198
    ) {
199
        $jwt = $values['id_token'];
200
        try {
201
            $idtoken = (new Parser())->parse((string) $jwt);
202
            $idtoken->getClaims();
203
            $amr = @$idtoken->getClaim('amr');
204
        } catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
205
        }
206
    }
207
208
    return $amr;
209
}
210