Failed Conditions
Pull Request — master (#63)
by Florent
07:25 queued 04:17
created

TokenEndpoint   C

Complexity

Total Complexity 15

Size/Duplication

Total Lines 177
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 19

Importance

Changes 0
Metric Value
wmc 15
lcom 1
cbo 19
dl 0
loc 177
rs 6.875
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 10 1
A process() 0 22 3
A createResponse() 0 11 2
A checkGrantType() 0 6 2
B issueAccessToken() 0 35 4
A getResourceOwner() 0 13 3
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * The MIT License (MIT)
7
 *
8
 * Copyright (c) 2014-2017 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\Server\Endpoint\Token;
15
16
use Http\Message\MessageFactory;
17
use Interop\Http\Server\RequestHandlerInterface;
18
use Interop\Http\Server\MiddlewareInterface;
19
use OAuth2Framework\Component\Server\Endpoint\Token\Processor\ProcessorManager;
20
use OAuth2Framework\Component\Server\GrantType\GrantTypeInterface;
21
use OAuth2Framework\Component\Server\Model\AccessToken\AccessToken;
22
use OAuth2Framework\Component\Server\Model\AccessToken\AccessTokenRepositoryInterface;
23
use OAuth2Framework\Component\Server\Model\Client\Client;
24
use OAuth2Framework\Component\Server\Model\Client\ClientId;
25
use OAuth2Framework\Component\Server\Model\Client\ClientRepositoryInterface;
26
use OAuth2Framework\Component\Server\Model\RefreshToken\RefreshTokenRepositoryInterface;
27
use OAuth2Framework\Component\Server\Model\ResourceOwner\ResourceOwnerId;
28
use OAuth2Framework\Component\Server\Model\ResourceOwner\ResourceOwnerInterface;
29
use OAuth2Framework\Component\Server\Model\UserAccount\UserAccountId;
30
use OAuth2Framework\Component\Server\Model\UserAccount\UserAccountRepositoryInterface;
31
use OAuth2Framework\Component\Server\Response\OAuth2Exception;
32
use OAuth2Framework\Component\Server\Response\OAuth2ResponseFactoryManager;
33
use Psr\Http\Message\ResponseInterface;
34
use Psr\Http\Message\ServerRequestInterface;
35
36
final class TokenEndpoint implements MiddlewareInterface
37
{
38
    /**
39
     * @var TokenEndpointExtensionManager
40
     */
41
    private $tokenEndpointExtensionManager;
42
43
    /**
44
     * @var ProcessorManager
45
     */
46
    private $processorManager;
47
48
    /**
49
     * @var ClientRepositoryInterface
50
     */
51
    private $clientRepository;
52
53
    /**
54
     * @var UserAccountRepositoryInterface
55
     */
56
    private $userAccountRepository;
57
58
    /**
59
     * @var MessageFactory
60
     */
61
    private $messageFactory;
62
63
    /**
64
     * @var AccessTokenRepositoryInterface
65
     */
66
    private $accessTokenRepository;
67
68
    /**
69
     * @var RefreshTokenRepositoryInterface
70
     */
71
    private $refreshTokenRepository;
72
73
    /**
74
     * TokenEndpoint constructor.
75
     *
76
     * @param ProcessorManager                $processorManager
77
     * @param ClientRepositoryInterface       $clientRepository
78
     * @param UserAccountRepositoryInterface  $userAccountRepository
79
     * @param TokenEndpointExtensionManager   $tokenEndpointExtensionManager
80
     * @param MessageFactory                  $messageFactory
81
     * @param AccessTokenRepositoryInterface  $accessTokenRepository
82
     * @param RefreshTokenRepositoryInterface $refreshTokenRepository
83
     */
84
    public function __construct(ProcessorManager $processorManager, ClientRepositoryInterface $clientRepository, UserAccountRepositoryInterface $userAccountRepository, TokenEndpointExtensionManager $tokenEndpointExtensionManager, MessageFactory $messageFactory, AccessTokenRepositoryInterface $accessTokenRepository, RefreshTokenRepositoryInterface $refreshTokenRepository)
85
    {
86
        $this->processorManager = $processorManager;
87
        $this->clientRepository = $clientRepository;
88
        $this->userAccountRepository = $userAccountRepository;
89
        $this->tokenEndpointExtensionManager = $tokenEndpointExtensionManager;
90
        $this->messageFactory = $messageFactory;
91
        $this->accessTokenRepository = $accessTokenRepository;
92
        $this->refreshTokenRepository = $refreshTokenRepository;
93
    }
94
95
    /**
96
     * {@inheritdoc}
97
     */
98
    public function process(ServerRequestInterface $request, RequestHandlerInterface $requestHandler)
99
    {
100
        $grantTypeData = GrantTypeData::create($request->getAttribute('client'));
101
102
        /** @var $grantType GrantTypeInterface From the dedicated middleware */
103
        $grantType = $request->getAttribute('grant_type');
104
        $grantType->checkTokenRequest($request);
105
        $grantTypeData = $grantType->prepareTokenResponse($request, $grantTypeData);
106
        if (null === $grantTypeData->getClient() || $grantTypeData->getClient()->isDeleted()) {
107
            throw new OAuth2Exception(401, ['error' => OAuth2ResponseFactoryManager::ERROR_INVALID_CLIENT, 'error_description' => 'Client authentication failed.']);
108
        }
109
110
        // This occurs now because the client may be found during the preparation process
111
        $this->checkGrantType($grantTypeData->getClient(), $grantType->getGrantType());
112
113
        $grantTypeData = $this->processorManager->handle($request, $grantTypeData, $grantType);
114
115
        $accessToken = $this->issueAccessToken($grantTypeData);
116
        $data = $this->tokenEndpointExtensionManager->process($grantTypeData->getClient(), $this->getResourceOwner($grantTypeData->getResourceOwnerId()), $accessToken);
117
118
        return $this->createResponse($data);
119
    }
120
121
    /**
122
     * @param array $data
123
     *
124
     * @return ResponseInterface
125
     */
126
    private function createResponse(array $data): ResponseInterface
127
    {
128
        $response = $this->messageFactory->createResponse();
129
        $response->getBody()->write(json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
130
        $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Cache-Control' => 'no-cache, no-store, max-age=0, must-revalidate, private', 'Pragma' => 'no-cache'];
131
        foreach ($headers as $k => $v) {
132
            $response = $response->withHeader($k, $v);
133
        }
134
135
        return $response;
136
    }
137
138
    /**
139
     * @param Client $client
140
     * @param string $grantType
141
     *
142
     * @throws OAuth2Exception
143
     */
144
    private function checkGrantType(Client $client, string $grantType)
145
    {
146
        if (!$client->isGrantTypeAllowed($grantType)) {
147
            throw new OAuth2Exception(400, ['error' => OAuth2ResponseFactoryManager::ERROR_UNAUTHORIZED_CLIENT, 'error_description' => sprintf('The grant type \'%s\' is unauthorized for this client.', $grantType)]);
148
        }
149
    }
150
151
    /**
152
     * @param GrantTypeData $grantTypeData
153
     *
154
     * @return AccessToken
155
     */
156
    private function issueAccessToken(GrantTypeData $grantTypeData): AccessToken
157
    {
158
        if ($grantTypeData->hasRefreshToken()) {
159
            $refreshToken = $this->refreshTokenRepository->create(
160
                $grantTypeData->getResourceOwnerId(),
161
                $grantTypeData->getClient()->getPublicId(),
162
                $grantTypeData->getParameters(),
163
                $grantTypeData->getMetadatas(),
164
                $grantTypeData->getScopes(),
165
                null,
166
                null
167
            );
168
        } else {
169
            $refreshToken = null;
170
        }
171
172
        $accessToken = $this->accessTokenRepository->create(
173
            $grantTypeData->getResourceOwnerId(),
174
            $grantTypeData->getClient()->getPublicId(),
175
            $grantTypeData->getParameters(),
176
            $grantTypeData->getMetadatas(),
177
            $grantTypeData->getScopes(),
178
            null === $refreshToken ? null : $refreshToken->getTokenId(),
179
            null,
180
            null
181
        );
182
183
        if (null !== $refreshToken) {
184
            $refreshToken = $refreshToken->addAccessToken($accessToken->getTokenId());
185
            $this->refreshTokenRepository->save($refreshToken);
186
        }
187
        $this->accessTokenRepository->save($accessToken);
188
189
        return $accessToken;
190
    }
191
192
    /**
193
     * @param ResourceOwnerId $resourceOwnerId
194
     *
195
     * @throws OAuth2Exception
196
     *
197
     * @return ResourceOwnerInterface
198
     */
199
    private function getResourceOwner(ResourceOwnerId $resourceOwnerId): ResourceOwnerInterface
200
    {
201
        $resourceOwner = $this->clientRepository->find(ClientId::create($resourceOwnerId->getValue()));
202
        if (null === $resourceOwner) {
203
            $resourceOwner = $this->userAccountRepository->findUserAccount(UserAccountId::create($resourceOwnerId->getValue()));
204
        }
205
206
        if (null === $resourceOwner) {
207
            throw new OAuth2Exception(400, ['error' => OAuth2ResponseFactoryManager::ERROR_INVALID_REQUEST, 'error_description' => 'Unable to find the associated resource owner.']);
208
        }
209
210
        return $resourceOwner;
211
    }
212
}
213