Passed
Push — develop ( 054886...bd89ad )
by nguereza
01:41
created

AuthorizationGrant::setAuthorizationServer()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 4
rs 10
1
<?php
2
3
/**
4
 * Platine OAuth2
5
 *
6
 * Platine OAuth2 is a library that implements the OAuth2 specification
7
 *
8
 * This content is released under the MIT License (MIT)
9
 *
10
 * Copyright (c) 2020 Platine OAuth2
11
 *
12
 * Permission is hereby granted, free of charge, to any person obtaining a copy
13
 * of this software and associated documentation files (the "Software"), to deal
14
 * in the Software without restriction, including without limitation the rights
15
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
 * copies of the Software, and to permit persons to whom the Software is
17
 * furnished to do so, subject to the following conditions:
18
 *
19
 * The above copyright notice and this permission notice shall be included in all
20
 * copies or substantial portions of the Software.
21
 *
22
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
 * SOFTWARE.
29
 */
30
31
declare(strict_types=1);
32
33
namespace Platine\OAuth2\Grant;
34
35
use Platine\Http\ResponseInterface;
36
use Platine\Http\ServerRequestInterface;
37
use Platine\OAuth2\AuthorizationServerInterface;
38
use Platine\OAuth2\Entity\Client;
39
use Platine\OAuth2\Entity\TokenOwnerInterface;
40
use Platine\OAuth2\Exception\OAuth2Exception;
41
use Platine\OAuth2\Response\RedirectResponse;
42
use Platine\OAuth2\Service\AccessTokenService;
43
use Platine\OAuth2\Service\AuthorizationCodeService;
44
use Platine\OAuth2\Service\RefreshTokenService;
45
46
/**
47
 * @class AuthorizationGrant
48
 * @package Platine\OAuth2\Grant
49
 */
50
class AuthorizationGrant extends BaseGrant implements AuthorizationServerAwareInterface
51
{
52
    public const GRANT_TYPE = 'authorization_code';
53
    public const GRANT_RESPONSE_TYPE = 'code';
54
55
    /**
56
     * The AuthorizationCodeService
57
     * @var AuthorizationCodeService
58
     */
59
    protected AuthorizationCodeService $authorizationCodeService;
60
61
    /**
62
     * The AccessTokenService
63
     * @var AccessTokenService
64
     */
65
    protected AccessTokenService $accessTokenService;
66
67
    /**
68
     * The RefreshTokenService
69
     * @var RefreshTokenService
70
     */
71
    protected RefreshTokenService $refreshTokenService;
72
73
    /**
74
     * The authorization server instance
75
     * @var AuthorizationServerInterface|null
76
     */
77
    protected ?AuthorizationServerInterface $authorizationServer = null;
78
79
    /**
80
     * Create new instance
81
     * @param AuthorizationCodeService $authorizationCodeService
82
     * @param AccessTokenService $accessTokenService
83
     * @param RefreshTokenService $refreshTokenService
84
     */
85
    public function __construct(
86
        AuthorizationCodeService $authorizationCodeService,
87
        AccessTokenService $accessTokenService,
88
        RefreshTokenService $refreshTokenService
89
    ) {
90
        $this->authorizationCodeService = $authorizationCodeService;
91
        $this->accessTokenService = $accessTokenService;
92
        $this->refreshTokenService = $refreshTokenService;
93
    }
94
95
        /**
96
     * {@inheritdoc}
97
     */
98
    public function createAuthorizationResponse(
99
        ServerRequestInterface $request,
100
        Client $client,
101
        ?TokenOwnerInterface $owner = null
102
    ): ResponseInterface {
103
        $queryParams = $request->getQueryParams();
104
105
        // We must validate some parameters first
106
        $responseType = $queryParams['response_type'] ?? null;
107
        if ($responseType !== self::GRANT_RESPONSE_TYPE) {
108
            throw OAuth2Exception::invalidRequest(sprintf(
109
                'The desired grant type must be "code", but "%s" was given',
110
                $responseType
111
            ));
112
        }
113
114
        // We try to fetch the redirect URI from query param as per spec,
115
        // and if none found, we just use the first redirect URI defined in the client
116
        $clientRedirectUris = $client->getRedirectUris();
117
        $redirectUri = $queryParams['redirect_uri'] ?? $clientRedirectUris[0];
118
119
        // If the redirect URI cannot be found in the list, we throw an error
120
        // as we don't want the user to be redirected to an unauthorized URL
121
        if ($client->hasRedirectUri($redirectUri) === false) {
122
            throw OAuth2Exception::invalidRequest('Redirect URI does not match the client registered one');
123
        }
124
125
        // Scope and state allow to perform additional validation
126
        $scope = $queryParams['scope'] ?? null;
127
        $state = $queryParams['state'] ?? null;
128
        $scopes = is_string($scope) ? explode(' ', $scope) : [];
129
        $authorizationCode = $this->authorizationCodeService->createToken(
130
            $redirectUri,
131
            $owner,
0 ignored issues
show
Bug introduced by
It seems like $owner can also be of type null; however, parameter $owner of Platine\OAuth2\Service\A...eService::createToken() does only seem to accept Platine\OAuth2\Entity\TokenOwnerInterface, 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

131
            /** @scrutinizer ignore-type */ $owner,
Loading history...
132
            $client,
133
            $scopes
134
        );
135
136
        $uri = http_build_query(array_filter([
137
            'code' => $authorizationCode->getToken(),
138
            'state' => $state,
139
        ]));
140
141
        return new RedirectResponse($redirectUri . '?' . $uri);
142
    }
143
144
    /**
145
     * {@inheritdoc}
146
     */
147
    public function createTokenResponse(
148
        ServerRequestInterface $request,
149
        ?Client $client = null,
150
        ?TokenOwnerInterface $owner = null
151
    ): ResponseInterface {
152
        $postParams = $request->getParsedBody();
153
        $code = $postParams['code'] ?? null;
154
155
        if ($code === null) {
156
            throw OAuth2Exception::invalidRequest('Could not find the authorization code in the request');
157
        }
158
159
        $authorizationCode = $this->authorizationCodeService->getToken($code);
160
        if ($authorizationCode === null || $authorizationCode->isExpired()) {
161
            throw OAuth2Exception::invalidGrant('Authorization code cannot be found or is expired');
162
        }
163
164
        $clientId = $postParams['client_id'] ?? null;
165
        if ($authorizationCode->getClient()->getId() !== $clientId) {
166
            throw OAuth2Exception::invalidRequest(
167
                'Authorization code\'s client does not match with the one that created the authorization code'
168
            );
169
        }
170
171
        // If owner is null, we reuse the same as the authorization code
172
        if ($owner === null) {
173
            $owner = $authorizationCode->getOwner();
174
        }
175
176
        $scopes = $authorizationCode->getScopes();
177
        $accessToken = $this->accessTokenService->createToken($owner, $client, $scopes);
0 ignored issues
show
Bug introduced by
It seems like $owner can also be of type null; however, parameter $owner of Platine\OAuth2\Service\A...nService::createToken() does only seem to accept Platine\OAuth2\Entity\TokenOwnerInterface, 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

177
        $accessToken = $this->accessTokenService->createToken(/** @scrutinizer ignore-type */ $owner, $client, $scopes);
Loading history...
Bug introduced by
It seems like $client can also be of type null; however, parameter $client of Platine\OAuth2\Service\A...nService::createToken() does only seem to accept Platine\OAuth2\Entity\Client, 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

177
        $accessToken = $this->accessTokenService->createToken($owner, /** @scrutinizer ignore-type */ $client, $scopes);
Loading history...
178
        // Before generating a refresh token, we must make sure the
179
        //  authorization server supports this grant
180
181
        $refreshToken = null;
182
        if (
183
            $this->authorizationServer !== null &&
184
            $this->authorizationServer->hasGrant(RefreshTokenGrant::GRANT_TYPE)
185
        ) {
186
            $refreshToken = $this->refreshTokenService->createToken($owner, $client, $scopes);
0 ignored issues
show
Bug introduced by
It seems like $owner can also be of type null; however, parameter $owner of Platine\OAuth2\Service\R...nService::createToken() does only seem to accept Platine\OAuth2\Entity\TokenOwnerInterface, 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

186
            $refreshToken = $this->refreshTokenService->createToken(/** @scrutinizer ignore-type */ $owner, $client, $scopes);
Loading history...
187
        }
188
189
        return $this->generateTokenResponse($accessToken, $refreshToken);
190
    }
191
192
    /**
193
     * {@inheritdoc}
194
     */
195
    public function setAuthorizationServer(
196
        AuthorizationServerInterface $authorizationServer
197
    ): void {
198
        $this->authorizationServer = $authorizationServer;
199
    }
200
201
    /**
202
     * {@inheritdoc}
203
     */
204
    public function allowPublicClients(): bool
205
    {
206
        return true;
207
    }
208
}
209