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

Google::getDefaultScopes()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
rs 9.9666
c 0
b 0
f 0
cc 1
nc 1
nop 0
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->multipleDomains()) {
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 static function isDomainExpression($str) {
131
        return preg_match('/[\(\|\*]/', $str) && !preg_match('!/!', $str);
132
    }
133
134
    protected function multipleDomains() {
135
        return strpos($this->hostedDomain, ',') !== FALSE || self::isDomainExpression($this->hostedDomain);
136
    }
137
138
    /**
139
     * @throws HostedDomainException If the domain does not match the configured domain.
140
     */
141
    protected function assertMatchingDomains($hostedDomain)
142
    {
143
        if ($this->hostedDomain === null) {
144
            // No hosted domain configured.
145
            return;
146
        }
147
148
        $domains = array_filter(explode(',', $this->hostedDomain));
149
        if (! $domains) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $domains of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
150
            // No hosted domains configured.
151
            return;
152
        }
153
154
        foreach ($domains as $whiteListedDomain) {
155
            if ($this->assertMatchingDomain($whiteListedDomain, $hostedDomain)) {
156
                return;
157
            }
158
        }
159
160
        throw HostedDomainException::notMatchingDomain($this->hostedDomain);
161
    }
162
163
    /**
164
     * @return bool Whether user-originating domain equals or matches $reference.
165
     */
166
    protected function assertMatchingDomain($reference, $hostedDomain)
167
    {
168
        if ($reference === '*' && $hostedDomain) {
169
            // Any hosted domain is allowed.
170
            return true;
171
        }
172
173
        if ($reference === $hostedDomain) {
174
            // Hosted domain is correct.
175
            return true;
176
        }
177
178
        if (self::isDomainExpression($reference) && @preg_match('/' . $reference . '/', $hostedDomain)) {
179
            // Hosted domain is correct.
180
            return true;
181
        }
182
    }
183
}
184