Completed
Pull Request — master (#76)
by
unknown
01:29
created

Google::getResourceOwnerDetailsUrl()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
namespace League\OAuth2\Client\Provider;
4
5
use League\OAuth2\Client\Exception\HostedDomainException;
6
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
7
use League\OAuth2\Client\Token\AccessToken;
8
use League\OAuth2\Client\Tool\BearerAuthorizationTrait;
9
use Psr\Http\Message\ResponseInterface;
10
11
class Google extends AbstractProvider
12
{
13
    use BearerAuthorizationTrait;
14
15
    /**
16
     * @var string If set, this will be sent to google as the "access_type" parameter.
17
     * @link https://developers.google.com/identity/protocols/OpenIDConnect#authenticationuriparameters
18
     */
19
    protected $accessType;
20
21
    /**
22
     * @var string Comma-separated list of domains or domain regular expressions.
23
     *             If only one regular value is passed, it will be sent to google as the "hd" parameter.
24
     * @link https://developers.google.com/identity/protocols/OpenIDConnect#authenticationuriparameters
25
     */
26
    protected $hostedDomain;
27
28
    /**
29
     * @var string If set, this will be sent to google as the "prompt" parameter.
30
     * @link https://developers.google.com/identity/protocols/OpenIDConnect#authenticationuriparameters
31
     */
32
    protected $prompt;
33
34
    /**
35
     * @var array List of scopes that will be used for authentication.
36
     * @link https://developers.google.com/identity/protocols/googlescopes
37
     */
38
    protected $scopes = [];
39
40
    public function getBaseAuthorizationUrl()
41
    {
42
        return 'https://accounts.google.com/o/oauth2/v2/auth';
43
    }
44
45
    public function getBaseAccessTokenUrl(array $params)
46
    {
47
        return 'https://www.googleapis.com/oauth2/v4/token';
48
    }
49
50
    public function getResourceOwnerDetailsUrl(AccessToken $token)
51
    {
52
        return 'https://openidconnect.googleapis.com/v1/userinfo';
53
    }
54
55
    protected function getAuthorizationParameters(array $options)
56
    {
57
        if (empty($options['hd']) && $this->hostedDomain && !$this->isHostedDomainMultiple()) {
58
            $options['hd'] = $this->hostedDomain;
59
        }
60
61
        if (empty($options['access_type']) && $this->accessType) {
62
            $options['access_type'] = $this->accessType;
63
        }
64
65
        if (empty($options['prompt']) && $this->prompt) {
66
            $options['prompt'] = $this->prompt;
67
        }
68
69
        // The "approval_prompt" option MUST be removed to prevent conflicts with non-empty "prompt".
70
        if (!empty($options['prompt'])) {
71
            $options['approval_prompt'] = null;
72
        }
73
74
        // Default scopes MUST be included for OpenID Connect.
75
        // Additional scopes MAY be added by constructor or option.
76
        $scopes = array_merge($this->getDefaultScopes(), $this->scopes);
77
78
        if (!empty($options['scope'])) {
79
            $scopes = array_merge($scopes, $options['scope']);
80
        }
81
82
        $options['scope'] = array_unique($scopes);
83
84
        return parent::getAuthorizationParameters($options);
85
    }
86
87
    protected function getDefaultScopes()
88
    {
89
        // "openid" MUST be the first scope in the list.
90
        return [
91
            'openid',
92
            'email',
93
            'profile',
94
        ];
95
    }
96
97
    protected function getScopeSeparator()
98
    {
99
        return ' ';
100
    }
101
102
    protected function checkResponse(ResponseInterface $response, $data)
103
    {
104
        // @codeCoverageIgnoreStart
105
        if (empty($data['error'])) {
106
            return;
107
        }
108
        // @codeCoverageIgnoreEnd
109
110
        $code = 0;
111
        $error = $data['error'];
112
113
        if (is_array($error)) {
114
            $code = $error['code'];
115
            $error = $error['message'];
116
        }
117
118
        throw new IdentityProviderException($error, $code, $data);
119
    }
120
121
    protected function createResourceOwner(array $response, AccessToken $token)
122
    {
123
        $user = new GoogleUser($response);
124
125
        $this->assertMatchingDomains($user->getHostedDomain());
126
127
        return $user;
128
    }
129
130
    protected function isDomainExpression($str)
131
    {
132
        return preg_match('/[\(\|\*]/', $str) && !preg_match('!/!', $str);
133
    }
134
135
    protected function isHostedDomainMultiple()
136
    {
137
        return strpos($this->hostedDomain, ',') !== FALSE || $this->isDomainExpression($this->hostedDomain);
138
    }
139
140
    /**
141
     * @throws HostedDomainException If the domain does not match the configured domain.
142
     */
143
    protected function assertMatchingDomains($hostedDomain)
144
    {
145
        if ($this->hostedDomain === null) {
146
            // No hosted domain configured.
147
            return;
148
        }
149
150
        $domains = array_filter(explode(',', $this->hostedDomain));
151
        if (empty($domains)) {
152
            // No hosted domains configured.
153
            return;
154
        }
155
156
        foreach ($domains as $whiteListedDomain) {
157
            if ($this->assertMatchingDomain($whiteListedDomain, $hostedDomain)) {
158
                return;
159
            }
160
        }
161
162
        throw HostedDomainException::notMatchingDomain($this->hostedDomain);
163
    }
164
165
    /**
166
     * @return bool Whether user-originating domain equals or matches $reference.
167
     */
168
    protected function assertMatchingDomain($reference, $hostedDomain)
169
    {
170
        if ($reference === '*' && $hostedDomain) {
171
            // Any hosted domain is allowed.
172
            return true;
173
        }
174
175
        if ($reference === $hostedDomain) {
176
            // Hosted domain is correct.
177
            return true;
178
        }
179
180
        if ($this->isDomainExpression($reference) && @preg_match('/' . $reference . '/', $hostedDomain)) {
181
            // Hosted domain is correct.
182
            return true;
183
        }
184
    }
185
}
186