getEndUserAuthorizationRequired()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 3
c 1
b 0
f 0
dl 0
loc 7
ccs 0
cts 4
cp 0
rs 10
cc 2
nc 2
nop 0
crap 6
1
<?php
2
3
namespace rhertogh\Yii2Oauth2Server\components\authorization\EndSession\base;
4
5
// phpcs:disable Generic.Files.LineLength.TooLong
6
use Lcobucci\JWT\Encoding\JoseEncoder;
7
use Lcobucci\JWT\Signer\Key\InMemory;
8
use Lcobucci\JWT\Signer\Rsa\Sha256;
9
use Lcobucci\JWT\Token\Parser;
10
use Lcobucci\JWT\Validation\Constraint\PermittedFor;
11
use Lcobucci\JWT\Validation\Constraint\RelatedTo;
12
use Lcobucci\JWT\Validation\Constraint\SignedWith;
13
use Lcobucci\JWT\Validation\Validator;
14
use League\OAuth2\Server\RedirectUriValidators\RedirectUriValidator;
15
use rhertogh\Yii2Oauth2Server\components\authorization\base\Oauth2BaseAuthorizationRequest;
16
use rhertogh\Yii2Oauth2Server\helpers\UrlHelper;
17
use rhertogh\Yii2Oauth2Server\interfaces\components\authorization\EndSession\Oauth2EndSessionAuthorizationRequestInterface;
18
use rhertogh\Yii2Oauth2Server\interfaces\models\Oauth2ClientInterface;
19
use yii\base\InvalidCallException;
20
use yii\web\BadRequestHttpException;
21
use yii\web\ForbiddenHttpException;
22
use yii\web\UnauthorizedHttpException;
0 ignored issues
show
Coding Style introduced by
Header blocks must be separated by a single blank line
Loading history...
23
// phpcs:enable Generic.Files.LineLength.TooLong
24
25
abstract class Oauth2BaseEndSessionAuthorizationRequest extends Oauth2BaseAuthorizationRequest implements
26
    Oauth2EndSessionAuthorizationRequestInterface
27
{
28
    /**
29
     * @var string|null
30
     */
31
    protected $_idTokenHint;
32
33
    /**
34
     * @var string|null
35
     */
36
    protected $_endSessionUrl;
37
38
    /**
39
     * @var bool
40
     */
41
    protected $_validatedRequest = false;
42
43
    /**
44
     * @var bool|null
45
     */
46
    protected $_endUserAuthorizationRequired = null;
47
48
    public function __serialize()
49
    {
50
        return array_merge(parent::__serialize(), [
51
            '_idTokenHint' => $this->_idTokenHint,
52
            '_endSessionUrl' => $this->_endSessionUrl,
53
        ]);
54
    }
55
56
    /**
57
     * @inheritDoc
58
     */
59
    public function getIdTokenHint()
60
    {
61
        return $this->_idTokenHint;
62
    }
63
64
    /**
65
     * @inheritDoc
66
     */
67
    public function setIdTokenHint($idTokenHint)
68
    {
69
        $this->_idTokenHint = $idTokenHint;
70
        $this->_validatedRequest = false;
71
        return $this;
72
    }
73
74
    public function setClientIdentifier($clientIdentifier)
75
    {
76
        $this->_validatedRequest = false;
77
        return parent::setClientIdentifier($clientIdentifier);
78
    }
79
80
    /**
81
     * @inheritDoc
82
     */
83
    public function getEndSessionUrl()
84
    {
85
        return $this->_endSessionUrl;
86
    }
87
88
    /**
89
     * @inheritDoc
90
     */
91
    public function setEndSessionUrl($endSessionUrl)
92
    {
93
        $this->_endSessionUrl = $endSessionUrl;
94
        return $this;
95
    }
96
97
    public function validateRequest()
98
    {
99
        $module = $this->getModule();
100
        $idTokenHint = $this->getIdTokenHint();
101
        $clientIdentifier = $this->getClientIdentifier();
102
        $identity = $module->getUserIdentity();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $identity is correct as $module->getUserIdentity() targeting rhertogh\Yii2Oauth2Serve...dule::getUserIdentity() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
103
        $postLogoutRedirectUri = $this->getRedirectUri();
104
105
        $endUserConfirmationMayBeSkipped = false;
106
107
        if ($idTokenHint) {
108
109
            $parser = new Parser(new JoseEncoder());
110
111
            $idToken = $parser->parse($idTokenHint);
112
113
            $validator = new Validator();
114
115
            if (
116
                !$validator->validate(
117
                    $idToken,
118
                    new SignedWith(new Sha256(), InMemory::plainText($module->getPublicKey()->getKeyContents()))
119
                )
120
            ) {
121
                throw new UnauthorizedHttpException('Invalid `id_token_hint` signature.');
122
            }
123
124
            if ($clientIdentifier) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $clientIdentifier of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
125
                if (!$validator->validate($idToken, new PermittedFor($clientIdentifier))) {
126
                    throw new UnauthorizedHttpException('Invalid "aud" claim in `id_token_hint`.');
127
                }
128
            } else {
129
                $audiences = $idToken->claims()->get('aud');
130
                if (count($audiences) === 1) {
0 ignored issues
show
Bug introduced by
It seems like $audiences can also be of type null; however, parameter $value of count() does only seem to accept Countable|array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

130
                if (count(/** @scrutinizer ignore-type */ $audiences) === 1) {
Loading history...
131
                    $clientIdentifier = $audiences[0];
132
                    $this->setClientIdentifier($clientIdentifier);
133
                } else {
134
                    throw new BadRequestHttpException(
135
                        'The `client_id` parameter is required when there are multiple audiences'
136
                        . ' in the "aud" claim of the `id_token_hint`.'
137
                    );
138
                }
139
            }
140
141
            if ($identity) {
0 ignored issues
show
introduced by
$identity is of type null, thus it always evaluated to false.
Loading history...
142
                if ($validator->validate($idToken, new RelatedTo((string)$identity->getIdentifier()))) {
143
                    $endUserConfirmationMayBeSkipped = true;
144
                }
145
            }
146
        } else {
147
            if (!$module->openIdConnectAllowAnonymousRpInitiatedLogout) {
148
                throw new BadRequestHttpException('The `id_token_hint` parameter is required.');
149
            }
150
        }
151
152
        if ($clientIdentifier) {
153
            $client = $module->getClientRepository()->getClientEntity($clientIdentifier);
154
            if (!$client || !$client->isEnabled()) {
155
                throw new ForbiddenHttpException('Client "' . $clientIdentifier . '" not found or disabled.');
156
            }
157
158
            if (
159
                !($client->getOpenIdConnectRpInitiatedLogout()
160
                    > Oauth2ClientInterface::OIDC_RP_INITIATED_LOGOUT_DISABLED)
161
            ) {
162
                throw new ForbiddenHttpException('Client "' . $clientIdentifier . '" is not allowed to initiated end-user logout.'); // phpcs:ignore Generic.Files.LineLength.TooLong
163
            }
164
        }
165
166
        if ($postLogoutRedirectUri) {
167
            if (!empty($client)) {
168
                $allowedPostLogoutRedirectUris = $client->getPostLogoutRedirectUris();
169
                $validatePostLogoutRedirectUri = $postLogoutRedirectUri;
170
                if ($client->isVariableRedirectUriQueryAllowed()) {
171
                    $validatePostLogoutRedirectUri = UrlHelper::stripQueryAndFragment($validatePostLogoutRedirectUri);
172
                }
173
174
                $validator = new RedirectUriValidator($allowedPostLogoutRedirectUris);
175
                if (!$validator->validateRedirectUri($validatePostLogoutRedirectUri)) {
176
                    throw new UnauthorizedHttpException('Invalid `post_logout_redirect_uri`.');
177
                }
178
            } else {
179
                throw new UnauthorizedHttpException('`post_logout_redirect_uri` is only allowed if the client is known.'); // phpcs:ignore Generic.Files.LineLength.TooLong
180
            }
181
        }
182
183
        $endUserAuthorizationRequired = !(
184
            $endUserConfirmationMayBeSkipped
0 ignored issues
show
introduced by
The condition $endUserConfirmationMayBeSkipped is always false.
Loading history...
185
            && isset($client)
186
            && (
187
                $client->getOpenIdConnectRpInitiatedLogout()
188
                    === Oauth2ClientInterface::OIDC_RP_INITIATED_LOGOUT_ENABLED_WITHOUT_CONFIRMATION
189
            )
190
        );
191
192
        $this->setEndUserAuthorizationRequired($endUserAuthorizationRequired);
193
        $this->_validatedRequest = true;
194
    }
195
196
    public function getEndUserAuthorizationRequired()
197
    {
198
        if (!$this->_validatedRequest) {
199
            throw new InvalidCallException('Request must be validated first');
200
        }
201
202
        return $this->_endUserAuthorizationRequired;
203
    }
204
205
    public function setEndUserAuthorizationRequired($endUserAuthorizationRequired)
206
    {
207
        $this->_endUserAuthorizationRequired = $endUserAuthorizationRequired;
208
        return $this;
209
    }
210
}
211