Passed
Push — master ( 0c1875...09be9f )
by Rutger
15:24 queued 43s
created

Oauth2OidcEndSessionAction   A

Complexity

Total Complexity 23

Size/Duplication

Total Lines 114
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 23
eloc 64
c 1
b 0
f 0
dl 0
loc 114
ccs 0
cts 62
cp 0
rs 10

1 Method

Rating   Name   Duplication   Size   Complexity  
F run() 0 107 23
1
<?php
2
3
namespace rhertogh\Yii2Oauth2Server\controllers\web\openidconnect;
4
5
use Lcobucci\JWT\Encoding\JoseEncoder;
6
use Lcobucci\JWT\Signer\Key\InMemory;
7
use Lcobucci\JWT\Signer\Rsa\Sha256;
8
use Lcobucci\JWT\Token\Parser;
9
use Lcobucci\JWT\Validation\Constraint\PermittedFor;
10
use Lcobucci\JWT\Validation\Constraint\RelatedTo;
11
use Lcobucci\JWT\Validation\Constraint\SignedWith;
12
use Lcobucci\JWT\Validation\Validator;
13
use League\OAuth2\Server\RedirectUriValidators\RedirectUriValidator;
14
use rhertogh\Yii2Oauth2Server\controllers\web\base\Oauth2BaseWebAction;
15
use rhertogh\Yii2Oauth2Server\controllers\web\Oauth2OidcController;
16
use rhertogh\Yii2Oauth2Server\helpers\UrlHelper;
17
use rhertogh\Yii2Oauth2Server\interfaces\models\external\user\Oauth2OidcUserInterface;
18
use Yii;
19
use yii\base\InvalidConfigException;
20
use yii\web\BadRequestHttpException;
21
use yii\web\ForbiddenHttpException;
22
use yii\web\Response;
23
use yii\web\UnauthorizedHttpException;
24
25
/**
26
 * @property Oauth2OidcController $controller
27
 */
28
class Oauth2OidcEndSessionAction extends Oauth2BaseWebAction
29
{
30
    /**
31
     * @see https://openid.net/specs/openid-connect-rpinitiated-1_0.html
32
     * @return Response
33
     * @throws InvalidConfigException
34
     */
35
    public function run()
36
    {
37
        $module = $this->controller->module;
38
        $request = Yii::$app->request;
0 ignored issues
show
Documentation Bug introduced by
It seems like Yii::app->request can also be of type yii\web\Request. However, the property $request is declared as type yii\console\Request. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
39
        $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...
40
41
        $logoutVerificationRequired = false;
42
43
        if (!$module->enableOpenIdConnect) {
44
            throw new ForbiddenHttpException('OpenID Connect is disabled.');
45
        }
46
47
        if ($identity && !($identity instanceof Oauth2OidcUserInterface)) {
0 ignored issues
show
introduced by
$identity is of type null, thus it always evaluated to false.
Loading history...
48
            throw new InvalidConfigException('In order to support OpenID Connect '
49
                . get_class($identity) . ' must implement ' . Oauth2OidcUserInterface::class);
50
        }
51
52
        $clientIdentifier = $this->getRequestParam($request, 'client_id');
0 ignored issues
show
Bug introduced by
It seems like $request can also be of type yii\console\Request; however, parameter $request of rhertogh\Yii2Oauth2Serve...tion::getRequestParam() does only seem to accept yii\web\Request, 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

52
        $clientIdentifier = $this->getRequestParam(/** @scrutinizer ignore-type */ $request, 'client_id');
Loading history...
53
        $idTokenHint = $this->getRequestParam($request, 'id_token_hint');
54
        $state = $this->getRequestParam($request, 'state');
55
        $postLogoutRedirectUri = $this->getRequestParam($request, 'post_logout_redirect_uri');
56
57
        // The `id_token_hint` is the OIDC id token (https://openid.net/specs/openid-connect-core-1_0.html#IDToken)
0 ignored issues
show
Coding Style introduced by
Inline comments must end in full-stops, exclamation marks, or question marks
Loading history...
58
        if ($idTokenHint) {
59
60
            $parser = new Parser(new JoseEncoder());
61
62
            $idToken = $parser->parse($idTokenHint);
0 ignored issues
show
Bug introduced by
It seems like $idTokenHint can also be of type array; however, parameter $jwt of Lcobucci\JWT\Token\Parser::parse() does only seem to accept string, 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

62
            $idToken = $parser->parse(/** @scrutinizer ignore-type */ $idTokenHint);
Loading history...
63
64
            $validator = new Validator();
65
66
            if (!$validator->validate($idToken, new SignedWith(
0 ignored issues
show
Coding Style introduced by
The first expression of a multi-line control structure must be on the line after the opening parenthesis
Loading history...
67
                new Sha256(),
68
                InMemory::plainText($module->getPublicKey()->getKeyContents())
69
            ))) {
0 ignored issues
show
Coding Style introduced by
Each line in a multi-line control structure must be indented at least once; expected at least 16 spaces, but found 12
Loading history...
Coding Style introduced by
The closing parenthesis of a multi-line control structure must be on the line after the last expression
Loading history...
70
                throw new UnauthorizedHttpException('Invalid `id_token_hint` signature.');
71
            }
72
73
            if ($clientIdentifier) {
74
                if (!$validator->validate($idToken, new PermittedFor($clientIdentifier))) {
0 ignored issues
show
Bug introduced by
It seems like $clientIdentifier can also be of type array; however, parameter $audience of Lcobucci\JWT\Validation\...ittedFor::__construct() does only seem to accept string, 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

74
                if (!$validator->validate($idToken, new PermittedFor(/** @scrutinizer ignore-type */ $clientIdentifier))) {
Loading history...
75
                    throw new UnauthorizedHttpException('Invalid "aud" claim in `id_token_hint`.');
76
                }
77
            } else {
78
                $audiences = $idToken->claims()->get('aud');
79
                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

79
                if (count(/** @scrutinizer ignore-type */ $audiences) === 1) {
Loading history...
80
                    $clientIdentifier = $audiences[0];
81
                } else {
82
                    throw new BadRequestHttpException(
83
                        'The `client_id` parameter is required when there are multiple audiences'
84
                        . ' in the "aud" claim of the `id_token_hint`.'
85
                    );
86
                }
87
            }
88
89
            if ($identity) {
0 ignored issues
show
introduced by
$identity is of type null, thus it always evaluated to false.
Loading history...
90
                if (!$validator->validate($idToken, new RelatedTo((string)$identity->getIdentifier()))) {
91
                    $logoutVerificationRequired = true;
92
                }
93
            }
94
        } else {
95
            if (!$module->openIdConnectAllowAnonymousEndSession) {
96
                throw new BadRequestHttpException('The `id_token_hint` parameter is required.');
97
            }
98
            $logoutVerificationRequired = true;
99
        }
100
101
        if ($clientIdentifier) {
102
            $client = $module->getClientRepository()->getClientEntity($clientIdentifier);
103
            if (!$client || !$client->isEnabled()) {
0 ignored issues
show
introduced by
$client is of type rhertogh\Yii2Oauth2Serve...s\Oauth2ClientInterface, thus it always evaluated to true.
Loading history...
104
                throw new ForbiddenHttpException('Client "' . $clientIdentifier . '" not found or disabled.');
105
            }
106
        }
107
108
        if (!$logoutVerificationRequired) {
109
            if (isset($client)) {
110
                if (!$client->getOpenIdConnectSkipLogoutValidation()) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $client does not seem to be defined for all execution paths leading up to this point.
Loading history...
111
                    $logoutVerificationRequired = true;
112
                }
113
            } else {
114
                $logoutVerificationRequired = true;
115
            }
116
        }
117
118
        if ($logoutVerificationRequired) {
119
            throw new \LogicException('Not yet implemented');
120
        }
121
122
        $module->logoutUser();
123
124
        if (empty($client) || empty($postLogoutRedirectUri)) {
125
            return $this->controller->goHome();
126
        }
127
128
        $allowedPostLogoutRedirectUris = $client->getPostLogoutRedirectUris();
129
        $validatePostLogoutRedirectUri = $postLogoutRedirectUri;
130
        if ($client->isVariableRedirectUriQueryAllowed()) {
131
            $validatePostLogoutRedirectUri = UrlHelper::stripQueryAndFragment($validatePostLogoutRedirectUri);
132
        }
133
134
        $validator = new RedirectUriValidator($allowedPostLogoutRedirectUris);
135
        if (!$validator->validateRedirectUri($validatePostLogoutRedirectUri)) {
0 ignored issues
show
Bug introduced by
It seems like $validatePostLogoutRedirectUri can also be of type array; however, parameter $redirectUri of League\OAuth2\Server\Red...::validateRedirectUri() does only seem to accept string, 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

135
        if (!$validator->validateRedirectUri(/** @scrutinizer ignore-type */ $validatePostLogoutRedirectUri)) {
Loading history...
136
            throw new UnauthorizedHttpException('Invalid `post_logout_redirect_uri`.');
137
        }
138
139
        $redirectUri = UrlHelper::addQueryParams($postLogoutRedirectUri, ['state' => $state]);
0 ignored issues
show
Bug introduced by
It seems like $postLogoutRedirectUri can also be of type array; however, parameter $url of rhertogh\Yii2Oauth2Serve...elper::addQueryParams() does only seem to accept string, 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

139
        $redirectUri = UrlHelper::addQueryParams(/** @scrutinizer ignore-type */ $postLogoutRedirectUri, ['state' => $state]);
Loading history...
140
141
        return $this->controller->redirect($redirectUri);
142
    }
143
}
144