Failed Conditions
Push — ng ( 75309d...bc6f2d )
by Florent
08:22
created

TokenEndpoint   B

Complexity

Total Complexity 17

Size/Duplication

Total Lines 193
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 16

Importance

Changes 0
Metric Value
wmc 17
lcom 1
cbo 16
dl 0
loc 193
rs 8.4614
c 0
b 0
f 0

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 1
B process() 0 49 5
A createResponse() 0 8 1
A issueAccessToken() 0 13 1
A getResourceOwner() 0 13 3
A updateWithTokenTypeParameters() 0 16 3
A isGrantTypeAllowedForTheClient() 0 9 3
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\TokenEndpoint;
15
16
use Http\Message\ResponseFactory;
17
use Psr\Http\Server\RequestHandlerInterface;
18
use Psr\Http\Server\MiddlewareInterface;
19
use OAuth2Framework\Component\Core\AccessToken\AccessToken;
20
use OAuth2Framework\Component\Core\AccessToken\AccessTokenRepository;
21
use OAuth2Framework\Component\Core\Client\Client;
22
use OAuth2Framework\Component\Core\Client\ClientId;
23
use OAuth2Framework\Component\Core\Client\ClientRepository;
24
use OAuth2Framework\Component\Core\ResourceOwner\ResourceOwnerId;
25
use OAuth2Framework\Component\Core\ResourceOwner\ResourceOwner;
26
use OAuth2Framework\Component\Core\UserAccount\UserAccountId;
27
use OAuth2Framework\Component\Core\UserAccount\UserAccountRepository;
28
use OAuth2Framework\Component\Core\Exception\OAuth2Exception;
29
use OAuth2Framework\Component\TokenEndpoint\Extension\TokenEndpointExtensionManager;
30
use OAuth2Framework\Component\TokenType\TokenType;
31
use Psr\Http\Message\ResponseInterface;
32
use Psr\Http\Message\ServerRequestInterface;
33
34
class TokenEndpoint implements MiddlewareInterface
35
{
36
    /**
37
     * @var TokenEndpointExtensionManager
38
     */
39
    private $tokenEndpointExtensionManager;
40
41
    /**
42
     * @var ClientRepository
43
     */
44
    private $clientRepository;
45
46
    /**
47
     * @var UserAccountRepository
48
     */
49
    private $userAccountRepository;
50
51
    /**
52
     * @var ResponseFactory
53
     */
54
    private $responseFactory;
55
56
    /**
57
     * @var AccessTokenRepository
58
     */
59
    private $accessTokenRepository;
60
61
    /**
62
     * TokenEndpoint constructor.
63
     *
64
     * @param ClientRepository              $clientRepository
65
     * @param UserAccountRepository         $userAccountRepository
66
     * @param TokenEndpointExtensionManager $tokenEndpointExtensionManager
67
     * @param ResponseFactory               $responseFactory
68
     * @param AccessTokenRepository         $accessTokenRepository
69
     */
70
    public function __construct(ClientRepository $clientRepository, UserAccountRepository $userAccountRepository, TokenEndpointExtensionManager $tokenEndpointExtensionManager, ResponseFactory $responseFactory, AccessTokenRepository $accessTokenRepository)
71
    {
72
        $this->clientRepository = $clientRepository;
73
        $this->userAccountRepository = $userAccountRepository;
74
        $this->tokenEndpointExtensionManager = $tokenEndpointExtensionManager;
75
        $this->responseFactory = $responseFactory;
76
        $this->accessTokenRepository = $accessTokenRepository;
77
    }
78
79
    /**
80
     * {@inheritdoc}
81
     */
82
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
83
    {
84
        // We prepare the Grant Type Data.
85
        // The client may be null (authenticated by other means).
86
        $grantTypeData = GrantTypeData::create($request->getAttribute('client'));
87
88
        // We retrieve the Grant Type.
89
        // This middleware must be behind the GrantTypeMiddleware
90
        $grantType = $request->getAttribute('grant_type');
91
        if (!$grantType instanceof GrantType) {
92
            throw new OAuth2Exception(500, OAuth2Exception::ERROR_INTERNAL, null);
93
        }
94
95
        // We check that the request has all parameters needed for the selected grant type
96
        $grantType->checkRequest($request);
97
98
        // The grant type prepare the token response
99
        // The grant type data should be updated accordingly
100
        $grantTypeData = $grantType->prepareResponse($request, $grantTypeData);
101
102
        // At this stage, the client should be authenticated
103
        // If not, we stop the authorization grant
104
        if (null === $grantTypeData->getClient() || $grantTypeData->getClient()->isDeleted()) {
105
            throw new OAuth2Exception(401, OAuth2Exception::ERROR_INVALID_CLIENT, 'Client authentication failed.');
106
        }
107
108
        // We check the client is allowed to use the selected grant type
109
        if (!$grantTypeData->getClient()->isGrantTypeAllowed($grantType->name())) {
110
            throw new OAuth2Exception(400, OAuth2Exception::ERROR_UNAUTHORIZED_CLIENT, sprintf('The grant type "%s" is unauthorized for this client.', $grantType->name()));
111
        }
112
113
        // We populate the token type parameters
114
        $grantTypeData = $this->updateWithTokenTypeParameters($request, $grantTypeData);
115
116
        // We call for extensions prior to the Access Token issuance
117
        $grantTypeData = $this->tokenEndpointExtensionManager->handleBeforeAccessTokenIssuance($request, $grantTypeData, $grantType);
118
119
        // We grant the client
120
        $grantTypeData = $grantType->grant($request, $grantTypeData);
121
122
        // Everything is fine so we can issue the access token
123
        $accessToken = $this->issueAccessToken($grantTypeData);
124
        $resourceOwner = $this->getResourceOwner($grantTypeData->getResourceOwnerId());
0 ignored issues
show
Bug introduced by
It seems like $grantTypeData->getResourceOwnerId() can be null; however, getResourceOwner() 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...
125
126
        // We call for extensions after to the Access Token issuance
127
        $data = $this->tokenEndpointExtensionManager->handleAfterAccessTokenIssuance($grantTypeData->getClient(), $resourceOwner, $accessToken);
128
129
        return $this->createResponse($data);
130
    }
131
132
    /**
133
     * @param array $data
134
     *
135
     * @return ResponseInterface
136
     */
137
    private function createResponse(array $data): ResponseInterface
138
    {
139
        $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Cache-Control' => 'no-cache, no-store, max-age=0, must-revalidate, private', 'Pragma' => 'no-cache'];
140
        $response = $this->responseFactory->createResponse(200, null, $headers);
141
        $response->getBody()->write(json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
142
143
        return $response;
144
    }
145
146
    /**
147
     * @param GrantTypeData $grantTypeData
148
     *
149
     * @return AccessToken
150
     */
151
    private function issueAccessToken(GrantTypeData $grantTypeData): AccessToken
152
    {
153
        $accessToken = $this->accessTokenRepository->create(
154
            $grantTypeData->getResourceOwnerId(),
0 ignored issues
show
Bug introduced by
It seems like $grantTypeData->getResourceOwnerId() can be null; however, create() 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...
155
            $grantTypeData->getClient()->getPublicId(),
156
            $grantTypeData->getParameters(),
157
            $grantTypeData->getMetadatas(),
158
            null
159
        );
160
        $this->accessTokenRepository->save($accessToken);
161
162
        return $accessToken;
163
    }
164
165
    /**
166
     * @param ResourceOwnerId $resourceOwnerId
167
     *
168
     * @throws OAuth2Exception
169
     *
170
     * @return ResourceOwner
171
     */
172
    private function getResourceOwner(ResourceOwnerId $resourceOwnerId): ResourceOwner
173
    {
174
        $resourceOwner = $this->clientRepository->find(ClientId::create($resourceOwnerId->getValue()));
175
        if (null === $resourceOwner) {
176
            $resourceOwner = $this->userAccountRepository->find(UserAccountId::create($resourceOwnerId->getValue()));
177
        }
178
179
        if (null === $resourceOwner) {
180
            throw new OAuth2Exception(400, OAuth2Exception::ERROR_INVALID_REQUEST, 'Unable to find the associated resource owner.');
181
        }
182
183
        return $resourceOwner;
184
    }
185
186
    /**
187
     * @param ServerRequestInterface $request
188
     * @param GrantTypeData          $grantTypeData
189
     *
190
     * @return GrantTypeData
191
     *
192
     * @throws OAuth2Exception
193
     */
194
    private function updateWithTokenTypeParameters(ServerRequestInterface $request, GrantTypeData $grantTypeData): GrantTypeData
195
    {
196
        /** @var TokenType $tokenType */
197
        $tokenType = $request->getAttribute('token_type');
198
        if (!$grantTypeData->getClient()->isTokenTypeAllowed($tokenType->name())) {
199
            throw new OAuth2Exception(400, OAuth2Exception::ERROR_INVALID_REQUEST, sprintf('The token type "%s" is not allowed for the client.', $tokenType->name()));
200
        }
201
202
        $info = $tokenType->getAdditionalInformation();
203
        $info['token_type'] = $tokenType->name();
204
        foreach ($info as $k => $v) {
205
            $grantTypeData = $grantTypeData->withParameter($k, $v);
206
        }
207
208
        return $grantTypeData;
209
    }
210
211
    /**
212
     * @param Client $client
213
     * @param string $grant_type
214
     *
215
     * @return bool
216
     */
217
    private function isGrantTypeAllowedForTheClient(Client $client, string $grant_type): bool
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
218
    {
219
        $grant_types = $client->has('grant_types') ? $client->get('grant_types') : [];
220
        if (!is_array($grant_types)) {
221
            throw new \InvalidArgumentException('The metadata "grant_types" must be an array.');
222
        }
223
224
        return in_array($grant_type, $grant_types);
225
    }
226
}
227