Completed
Push — master ( aaba5a...c6baf0 )
by Florent
04:07
created

OpenIdConnectExtension::hasOpenIdScope()   A

Complexity

Conditions 2
Paths 2

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 2
nc 2
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
     *
58
     * @param IdTokenBuilderFactory $idTokenBuilderFactory
59
     * @param string                $defaultSignatureAlgorithm
60
     * @param JWSBuilder            $jwsBuilder
61
     * @param JWKSet                $signatureKeys
62
     */
63
    public function __construct(IdTokenBuilderFactory $idTokenBuilderFactory, string $defaultSignatureAlgorithm, JWSBuilder $jwsBuilder, JWKSet $signatureKeys)
64
    {
65
        $this->jwsBuilder = $jwsBuilder;
66
        $this->signatureKeys = $signatureKeys;
67
        $this->idTokenBuilderFactory = $idTokenBuilderFactory;
68
        $this->defaultSignatureAlgorithm = $defaultSignatureAlgorithm;
69
    }
70
71
    /**
72
     * @param JWEBuilder $jweBuilder
73
     */
74
    public function enableEncryption(JWEBuilder $jweBuilder)
75
    {
76
        $this->jweBuilder = $jweBuilder;
77
    }
78
79
    public function beforeAccessTokenIssuance(ServerRequestInterface $request, GrantTypeData $grantTypeData, GrantType $grantType, callable $next): GrantTypeData
80
    {
81
        return $next($request, $grantTypeData, $grantType);
82
    }
83
84
    public function afterAccessTokenIssuance(Client $client, ResourceOwner $resourceOwner, AccessToken $accessToken, callable $next): array
85
    {
86
        $data = $next($client, $resourceOwner, $accessToken);
87
        if ($resourceOwner instanceof UserAccount && $this->hasOpenIdScope($accessToken) && $accessToken->getMetadata()->has('redirect_uri')) {
88
            $idToken = $this->issueIdToken($client, $resourceOwner, $accessToken);
89
            $data['id_token'] = $idToken;
90
        }
91
92
        return $data;
93
    }
94
95
    /**
96
     * @param Client      $client
97
     * @param UserAccount $userAccount
98
     * @param AccessToken $accessToken
99
     *
100
     * @return string
101
     */
102
    private function issueIdToken(Client $client, UserAccount $userAccount, AccessToken $accessToken): string
103
    {
104
        $redirectUri = $accessToken->getMetadata()->get('redirect_uri');
105
        $idTokenBuilder = $this->idTokenBuilderFactory->createBuilder($client, $userAccount, $redirectUri);
106
107
        $requestedClaims = $this->getIdTokenClaims($accessToken);
108
        $idTokenBuilder = $idTokenBuilder->withRequestedClaims($requestedClaims);
109
110
        $idTokenBuilder = $idTokenBuilder->withAccessTokenId($accessToken->getAccessTokenId());
111
112
        if ($client->has('id_token_signed_response_alg')) {
113
            $signatureAlgorithm = $client->get('id_token_signed_response_alg');
114
            $idTokenBuilder = $idTokenBuilder->withSignature($this->jwsBuilder, $this->signatureKeys, $signatureAlgorithm);
115
        } else {
116
            $idTokenBuilder = $idTokenBuilder->withSignature($this->jwsBuilder, $this->signatureKeys, $this->defaultSignatureAlgorithm);
117
        }
118
        if ($client->has('userinfo_encrypted_response_alg') && $client->has('userinfo_encrypted_response_enc') && null !== $this->jweBuilder) {
119
            $keyEncryptionAlgorithm = $client->get('userinfo_encrypted_response_alg');
120
            $contentEncryptionAlgorithm = $client->get('userinfo_encrypted_response_enc');
121
            $idTokenBuilder = $idTokenBuilder->withEncryption($this->jweBuilder, $keyEncryptionAlgorithm, $contentEncryptionAlgorithm);
122
        }
123
        if ($client->has('require_auth_time')) {
124
            $idTokenBuilder->withAuthenticationTime();
125
        }
126
        $idTokenBuilder = $idTokenBuilder->withAccessToken($accessToken);
127
128
        return $idTokenBuilder->build();
129
    }
130
131
    /**
132
     * @param AccessToken $accessToken
133
     *
134
     * @return array
135
     */
136
    private function getIdTokenClaims(AccessToken $accessToken): array
137
    {
138
        if (!$accessToken->getMetadata()->has('requested_claims')) {
139
            return [];
140
        }
141
142
        $requestedClaims = $accessToken->getMetadata()->get('requested_claims');
143
        $requestedClaims = \json_decode($requestedClaims, true);
144
        if (!\is_array($requestedClaims)) {
145
            throw new \InvalidArgumentException('Invalid claim request');
146
        }
147
        if (true === \array_key_exists('id_token', $requestedClaims)) {
148
            return $requestedClaims['id_token'];
149
        }
150
151
        return [];
152
    }
153
154
    private function hasOpenIdScope(AccessToken $accessToken): bool
155
    {
156
        return $accessToken->getParameter()->has('scope') && \in_array('openid', \explode(' ', $accessToken->getParameter()->get('scope')), true);
157
    }
158
}
159