OAuthPresenter::issueAuthorizationCode()   A
last analyzed

Complexity

Conditions 6
Paths 20

Size

Total Lines 21
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 15
nc 20
nop 4
dl 0
loc 21
rs 9.2222
c 0
b 0
f 0
1
<?php
2
3
namespace kalanis\OAuth2\Application;
4
5
6
use kalanis\OAuth2\Exceptions\InvalidGrantException;
7
use kalanis\OAuth2\Exceptions\InvalidStateException;
8
use kalanis\OAuth2\Exceptions\OAuthException;
9
use kalanis\OAuth2\Exceptions\UnauthorizedClientException;
10
use kalanis\OAuth2\Exceptions\UnsupportedResponseTypeException;
11
use kalanis\OAuth2\Grant\GrantContext;
12
use kalanis\OAuth2\Grant\GrantType;
13
use kalanis\OAuth2\Grant\IGrant;
14
use kalanis\OAuth2\Storage\AuthorizationCodes\AuthorizationCodeFacade;
15
use kalanis\OAuth2\Storage\Clients\IClient;
16
use kalanis\OAuth2\Storage\Clients\IClientStorage;
17
use kalanis\OAuth2\Storage\Exceptions\InvalidAuthorizationCodeException;
18
use kalanis\OAuth2\Storage\Exceptions\TokenException;
19
use Nette\Application\Responses\JsonResponse;
20
use Nette\Application\UI\Presenter;
21
use Nette\Http\Url;
22
use Traversable;
23
24
25
/**
26
 * OauthPresenter
27
 * @package kalanis\OAuth2\Application
28
 */
29
class OAuthPresenter extends Presenter implements IOAuthPresenter
30
{
31
32
    /** Inject token manager - authorization code */
33
    #[\Nette\DI\Attributes\Inject]
34
    public AuthorizationCodeFacade $authorizationCode;
35
36
    /** Inject client storage */
37
    #[\Nette\DI\Attributes\Inject]
38
    public IClientStorage $clientStorage;
39
40
    /** Inject grant strategy context */
41
    #[\Nette\DI\Attributes\Inject]
42
    public GrantContext $grantContext;
43
44
    protected ?IClient $client = null;
45
46
    /**
47
     * @param string $responseType
48
     * @param string $redirectUrl
49
     * @param string|null $scope
50
     * @param string|null $state
51
     */
52
    public function issueAuthorizationCode(string $responseType, string $redirectUrl, ?string $scope = null, ?string $state = null): void
53
    {
54
        try {
55
            if ('code' !== $responseType) {
56
                throw new UnsupportedResponseTypeException;
57
            }
58
            if (empty($this->client?->getId())) {
59
                throw new UnauthorizedClientException;
60
            }
61
62
            $scope = array_filter(explode(',', str_replace(' ', ',', strval($scope))));
63
            $code = $this->authorizationCode->create($this->client, $this->getUser()->getId(), $scope);
0 ignored issues
show
Bug introduced by
It seems like $this->client can also be of type null; however, parameter $client of kalanis\OAuth2\Storage\A...ionCodeFacade::create() does only seem to accept kalanis\OAuth2\Storage\Clients\IClient, 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

63
            $code = $this->authorizationCode->create(/** @scrutinizer ignore-type */ $this->client, $this->getUser()->getId(), $scope);
Loading history...
64
            $data = ['code' => $code->getAuthorizationCode()];
65
            if (!empty($state)) {
66
                $data['state'] = $state;
67
            }
68
            $this->oauthResponse($data, $redirectUrl);
69
        } catch (OAuthException $e) {
70
            $this->oauthError($e);
71
        } catch (TokenException) {
72
            $this->oauthError(new InvalidGrantException);
73
        }
74
    }
75
76
    /**
77
     * Send OAuth response
78
     * @param iterable<string|int, mixed> $data
79
     * @param string|null $redirectUrl
80
     * @param int $code
81
     * @return void
82
     */
83
    public function oauthResponse(iterable $data, ?string $redirectUrl = null, int $code = 200): void
84
    {
85
        if ($data instanceof Traversable) {
86
            $data = iterator_to_array($data);
87
        }
88
        $data = (array) $data;
89
90
        // Redirect, if there is URL
91
        if (null !== $redirectUrl) {
92
            $url = new Url($redirectUrl);
93
            if ('token' == $this->getParameter('response_type')) {
94
                $url->setFragment(http_build_query($data));
95
            } else {
96
                $url->appendQuery($data);
97
            }
98
            $this->redirectUrl($url);
99
        }
100
101
        $this->getHttpResponse()->setCode($code);
102
        $this->sendResponse(new JsonResponse($data));
103
    }
104
105
    /**
106
     * Provide OAuth2 error response (redirect or at least JSON)
107
     */
108
    public function oauthError(OAuthException $exception): void
109
    {
110
        $error = ['error' => $exception->getKey(), 'error_description' => $exception->getMessage()];
111
        $redirect = $this->getParameter('redirect_uri');
112
        $this->oauthResponse(
113
            $error,
114
            empty($redirect) ? null : strval($redirect),
115
            $exception->getCode()
116
        );
117
    }
118
119
    /**
120
     * Issue access token to client
121
     * @param string|null $grantType
122
     * @param string|null $redirectUrl
123
     * @throws InvalidAuthorizationCodeException
124
     * @throws InvalidStateException
125
     */
126
    public function issueAccessToken(?string $grantType = null, ?string $redirectUrl = null): void
127
    {
128
        try {
129
            if (null !== $grantType) {
130
                $grantType = $this->grantContext->getGrantType($grantType);
131
            } else {
132
                $grantType = $this->getGrantType();
133
            }
134
135
            $response = $grantType->getAccessToken();
136
            $this->oauthResponse($response, $redirectUrl);
137
        } catch (OAuthException $e) {
138
            $this->oauthError($e);
139
        } catch (TokenException) {
140
            $this->oauthError(new InvalidGrantException);
141
        }
142
    }
143
144
    /**
145
     * Get grant type
146
     * @throws UnsupportedResponseTypeException
147
     * @return IGrant
148
     */
149
    public function getGrantType(): IGrant
150
    {
151
        $request = $this->getHttpRequest();
152
        $grantType = strval($request->getPost(GrantType::GRANT_TYPE_KEY));
153
        try {
154
            return $this->grantContext->getGrantType($grantType);
155
        } catch (InvalidStateException $e) {
156
            throw new UnsupportedResponseTypeException('Trying to use unknown grant type ' . $grantType, $e);
157
        }
158
    }
159
160
    /**
161
     * On presenter startup
162
     * @throws InvalidGrantException
163
     * @return void
164
     */
165
    protected function startup(): void
166
    {
167
        parent::startup();
168
        $clientId = $this->getParameter(GrantType::CLIENT_ID_KEY);
169
        $clientSecret = $this->getParameter(GrantType::CLIENT_SECRET_KEY);
170
        $client = $this->clientStorage->getClient(
171
            is_numeric($clientId) ? intval($clientId) : strval($clientId),
172
            is_null($clientSecret) ? null : strval($clientSecret)
173
        );
174
        if (is_null($client)) {
175
            throw new InvalidGrantException;
176
        }
177
        $this->client = $client;
178
    }
179
}
180