Failed Conditions
Push — master ( cde4a0...52dcf4 )
by Florent
08:54
created

OpenIdConnectExtension::getIdTokenClaims()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 13
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 7
nc 3
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * The MIT License (MIT)
7
 *
8
 * Copyright (c) 2014-2018 Spomky-Labs
9
 *
10
 * This software may be modified and distributed under the terms
11
 * of the MIT license.  See the LICENSE file for details.
12
 */
13
14
namespace OAuth2Framework\Component\OpenIdConnect;
15
16
use Jose\Component\Core\JWKSet;
17
use Jose\Component\Encryption\JWEBuilder;
18
use Jose\Component\Signature\JWSBuilder;
19
use OAuth2Framework\Component\Core\AccessToken\AccessToken;
20
use OAuth2Framework\Component\Core\Client\Client;
21
use OAuth2Framework\Component\Core\ResourceOwner\ResourceOwner;
22
use OAuth2Framework\Component\Core\UserAccount\UserAccount;
23
use OAuth2Framework\Component\TokenEndpoint\Extension\TokenEndpointExtension;
24
use OAuth2Framework\Component\TokenEndpoint\GrantType;
25
use OAuth2Framework\Component\TokenEndpoint\GrantTypeData;
26
use Psr\Http\Message\ServerRequestInterface;
27
28
class OpenIdConnectExtension implements TokenEndpointExtension
29
{
30
    /**
31
     * @var JWKSet|null
32
     */
33
    private $signatureKeys = null;
34
35
    /**
36
     * @var JWSBuilder|null
37
     */
38
    private $jwsBuilder = null;
39
40
    /**
41
     * @var JWEBuilder|null
42
     */
43
    private $jweBuilder = null;
44
45
    /**
46
     * @var IdTokenBuilderFactory
47
     */
48
    private $idTokenBuilderFactory;
49
50
    /**
51
     * @var string
52
     */
53
    private $defaultSignatureAlgorithm;
54
55
    /**
56
     * OpenIdConnectExtension constructor.
57
     * @param IdTokenBuilderFactory $idTokenBuilderFactory
58
     * @param string $defaultSignatureAlgorithm
59
     * @param JWSBuilder $jwsBuilder
60
     * @param JWKSet $signatureKeys
61
     */
62
    public function __construct(IdTokenBuilderFactory $idTokenBuilderFactory, string $defaultSignatureAlgorithm, JWSBuilder $jwsBuilder, JWKSet $signatureKeys)
63
    {
64
        $this->jwsBuilder = $jwsBuilder;
65
        $this->signatureKeys = $signatureKeys;
66
        $this->idTokenBuilderFactory = $idTokenBuilderFactory;
67
        $this->defaultSignatureAlgorithm = $defaultSignatureAlgorithm;
68
    }
69
70
    /**
71
     * @param JWEBuilder $jweBuilder
72
     */
73
    public function enableEncryption(JWEBuilder $jweBuilder)
74
    {
75
        $this->jweBuilder = $jweBuilder;
76
    }
77
78
    public function beforeAccessTokenIssuance(ServerRequestInterface $request, GrantTypeData $grantTypeData, GrantType $grantType, callable $next): GrantTypeData
79
    {
80
        return $next($request, $grantTypeData, $grantType);
81
    }
82
83
    public function afterAccessTokenIssuance(Client $client, ResourceOwner $resourceOwner, AccessToken $accessToken, callable $next): array
84
    {
85
        if ($resourceOwner instanceof UserAccount && $this->accessTokenOpenIdHasScope($accessToken) && $accessToken->hasMetadata('redirect_uri')) {
86
            $idToken = $this->issueIdToken($client, $resourceOwner, $accessToken);
87
            $data = $next($client, $resourceOwner, $accessToken);
88
            $data['id_token'] = $idToken;
89
90
            return $data;
91
        }
92
93
        return $next($client, $resourceOwner, $accessToken);
94
    }
95
96
    /**
97
     * @param Client      $client
98
     * @param UserAccount $userAccount
99
     * @param AccessToken $accessToken
100
     *
101
     * @return string
102
     */
103
    private function issueIdToken(Client $client, UserAccount $userAccount, AccessToken $accessToken): string
104
    {
105
        $redirectUri = $accessToken->getMetadata('redirect_uri');
106
        $idTokenBuilder = $this->idTokenBuilderFactory->createBuilder($client, $userAccount, $redirectUri);
107
108
        $requestedClaims = $this->getIdTokenClaims($accessToken);
109
        $idTokenBuilder = $idTokenBuilder->withRequestedClaims($requestedClaims);
110
111
        $idTokenBuilder = $idTokenBuilder->withAccessTokenId($accessToken->getAccessTokenId());
112
113
        if ($client->has('id_token_signed_response_alg')) {
114
            $signatureAlgorithm = $client->get('id_token_signed_response_alg');
115
            $idTokenBuilder = $idTokenBuilder->withSignature($this->jwsBuilder, $this->signatureKeys, $signatureAlgorithm);
0 ignored issues
show
Bug introduced by
It seems like $this->jwsBuilder can be null; however, withSignature() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
Bug introduced by
It seems like $this->signatureKeys can be null; however, withSignature() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
116
        } else {
117
            $idTokenBuilder = $idTokenBuilder->withSignature($this->jwsBuilder, $this->signatureKeys, $this->defaultSignatureAlgorithm);
0 ignored issues
show
Bug introduced by
It seems like $this->jwsBuilder can be null; however, withSignature() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
Bug introduced by
It seems like $this->signatureKeys can be null; however, withSignature() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
118
        }
119
        if ($client->has('userinfo_encrypted_response_alg') && $client->has('userinfo_encrypted_response_enc') && null !== $this->jweBuilder) {
120
            $keyEncryptionAlgorithm = $client->get('userinfo_encrypted_response_alg');
121
            $contentEncryptionAlgorithm = $client->get('userinfo_encrypted_response_enc');
122
            $idTokenBuilder = $idTokenBuilder->withEncryption($this->jweBuilder, $keyEncryptionAlgorithm, $contentEncryptionAlgorithm);
123
        }
124
        $idTokenBuilder = $idTokenBuilder->withAccessToken($accessToken);
125
126
        return $idTokenBuilder->build();
127
    }
128
129
    /**
130
     * @param AccessToken $accessToken
131
     *
132
     * @return array
133
     */
134
    private function getIdTokenClaims(AccessToken $accessToken): array
135
    {
136
        if (!$accessToken->hasMetadata('requested_claims')) {
137
            return [];
138
        }
139
140
        $requestedClaims = $accessToken->getMetadata('requested_claims');
141
        if (true === array_key_exists('id_token', $requestedClaims)) {
142
            return $requestedClaims['id_token'];
143
        }
144
145
        return [];
146
    }
147
148
    private function accessTokenOpenIdHasScope(AccessToken $accessToken): bool
149
    {
150
        return $accessToken->hasParameter('scope') && !in_array('openid', explode(' ', $accessToken->getParameter('scope')));
151
    }
152
}