AuthorizationServer::checkIfAClientIsProvided()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
/**
3
 * @author Boris Guéry <[email protected]>
4
 */
5
6
namespace Bgy\OAuth2;
7
8
use Bgy\OAuth2\GrantType\GrantDecision;
9
use Bgy\OAuth2\GrantType\GrantError;
10
use Bgy\OAuth2\GrantType\GrantType;
11
use Bgy\OAuth2\Storage\AccessTokenStorage;
12
use Bgy\OAuth2\Storage\ClientStorage;
13
use Bgy\OAuth2\Storage\RefreshTokenStorage;
14
15
class AuthorizationServer
16
{
17
    private $configuration;
18
    private $clientStorage;
19
    private $accessTokenStorage;
20
    private $refreshTokenStorage;
21
    private $clientAuthenticator;
22
    private $grantTypeExtensions;
23
24
    public function __construct(AuthorizationServerConfiguration $configuration,
25
                                ClientStorage $clientStorage,
26
                                AccessTokenStorage $accessTokenStorage,
27
                                RefreshTokenStorage $refreshTokenStorage,
28
                                array $grantTypeExtensions)
29
    {
30
        $this->configuration       = $configuration;
31
        $this->clientStorage       = $clientStorage;
32
        $this->accessTokenStorage  = $accessTokenStorage;
33
        $this->refreshTokenStorage = $refreshTokenStorage;
34
        $this->grantTypeExtensions = $grantTypeExtensions;
35
        $this->clientAuthenticator = new ClientAuthenticator($clientStorage);
36
    }
37
38
    /**
39
     * @param TokenRequestAttempt $tokenRequestAttempt
40
     * @return FailedTokenRequestAttemptResult|SuccessfulTokenRequestAttemptResult
41
     */
42
    public function requestAccessToken(TokenRequestAttempt $tokenRequestAttempt)
43
    {
44
        if (!$this->checkGrantType($tokenRequestAttempt)) {
45
46
            return new FailedTokenRequestAttemptResult(GrantDecision::denied(GrantError::invalidGrant('Unknown grant type')));
47
        }
48
49
        if ($this->checkIfAClientIsAlwaysRequired()) {
50
51
            if (!$this->checkIfAClientIsProvided($tokenRequestAttempt)) {
52
53
                return new FailedTokenRequestAttemptResult(GrantDecision::denied(GrantError::invalidGrant('Missing client_id')));
54
            }
55
56
            if (!$this->checkIfTheProvidedClientIsValid($tokenRequestAttempt)) {
57
58
                return new FailedTokenRequestAttemptResult(GrantDecision::denied(GrantError::accessDenied('Invalid client credentials')));
59
            }
60
61
            if (!$this->checkIfClientSupportsRequestedGrantType($tokenRequestAttempt)) {
62
63
                return new FailedTokenRequestAttemptResult(
64
                    GrantDecision::denied(
65
                        GrantError::invalidGrant(
66
                            sprintf(
67
                                'This client doesn\'t support the following grant type: "%s"',
68
                                $tokenRequestAttempt->getGrantType()
69
                            )
70
                        )
71
                    )
72
                );
73
            }
74
        }
75
76
        $grantDecision = $this->getGrantTypeByIdentifier($tokenRequestAttempt->getGrantType())
77
            ->grant($tokenRequestAttempt)
78
        ;
79
80
        if ($grantDecision->equals(GrantDecision::allowed())) {
81
82
            $accessToken = $this->buildAccessToken($tokenRequestAttempt, $grantDecision);
83
            $refreshToken = $this->buildRefreshToken($accessToken);
84
85
            return new SuccessfulTokenRequestAttemptResult($grantDecision, $accessToken, $refreshToken);
86
        }
87
88
        return new FailedTokenRequestAttemptResult($grantDecision);
89
    }
90
91
    private function checkGrantType(TokenRequestAttempt $tokenRequestAttempt)
92
    {
93
        return (null !== $this->getGrantTypeByIdentifier($tokenRequestAttempt->getGrantType()));
94
    }
95
96
    private function checkIfAClientIsAlwaysRequired()
97
    {
98
        return $this->configuration->alwaysRequireAClient();
99
    }
100
101
    private function checkIfAClientIsProvided(TokenRequestAttempt $tokenRequestAttempt)
102
    {
103
        return (null !== $tokenRequestAttempt->getInputData()->getClientId());
104
    }
105
106
    private function checkIfTheProvidedClientIsValid(TokenRequestAttempt $tokenRequestAttempt)
107
    {
108
        return $this->clientAuthenticator->isClientValid(
109
            $tokenRequestAttempt->getInputData()->getClientId(),
110
            $tokenRequestAttempt->getInputData()->getClientSecret()
111
        );
112
    }
113
114
    private function checkIfClientSupportsRequestedGrantType(TokenRequestAttempt $tokenRequestAttempt)
115
    {
116
        $client = $this->clientStorage
117
            ->findById($tokenRequestAttempt->getInputData()->getClientId())
118
        ;
119
120
        return in_array($tokenRequestAttempt->getGrantType(), $client->getAllowedGrantTypes());
121
    }
122
123
    private function buildAccessToken(TokenRequestAttempt $tokenRequestAttempt, GrantDecision $grantDecision)
124
    {
125
        if ($grantDecision->isDenied()) {
126
127
            throw new \LogicException('Unable to build an access token with a denied decision');
128
        }
129
130
        $token = $this->configuration->getTokenGenerator()->generate(
131
            ['length' => $this->configuration->getAccessTokenLength()]
132
        )
133
        ;
134
135
        $expiresAt = new \DateTime('now', new \DateTimeZone('UTC'));
136
        $expiresAt->add(
137
            \DateInterval::createFromDateString(
138
                sprintf(
139
                    "%d seconds",
140
                    $this->configuration->getAccessTokenTTL()
141
                )
142
            )
143
        );
144
145
        $accessToken = new AccessToken(
146
            $token,
147
            \DateTimeImmutable::createFromMutable($expiresAt),
148
            $tokenRequestAttempt->getInputData()->getClientId(),
149
            $grantDecision->getResourceOwner(),
150
            []
151
        );
152
153
        $this->accessTokenStorage->save($accessToken);
154
155
        return $accessToken;
156
    }
157
158
    private function buildRefreshToken(AccessToken $accessToken)
159
    {
160
        $refreshToken = null;
161
        if ($this->configuration->alwaysGenerateARefreshToken()) {
162
            $token = $this->configuration->getTokenGenerator()->generate(
163
                ['length' => $this->configuration->getAccessTokenLength()]
164
            )
165
            ;
166
            $expiresAt = new \DateTime('now', new \DateTimeZone('UTC'));
167
            $expiresAt->add(
168
                \DateInterval::createFromDateString(
169
                    sprintf(
170
                        "%d seconds",
171
                        $this->configuration->getRefreshTokenTTL()
172
                    )
173
                )
174
            );
175
176
            $refreshToken = new RefreshToken(
177
                $token,
178
                $accessToken,
179
                \DateTimeImmutable::createFromMutable($expiresAt)
180
            );
181
182
            $this->refreshTokenStorage->save($refreshToken);
183
        }
184
185
        return $refreshToken;
186
    }
187
188
    /**
189
     * @param $identifier
190
     * @return GrantType
191
     */
192
    private function getGrantTypeByIdentifier($identifier)
193
    {
194
        foreach ($this->grantTypeExtensions as $grantTypeExtension) {
195
            if (strtolower($identifier) === strtolower($grantTypeExtension->getIdentifier())) {
196
197
                return $grantTypeExtension;
198
            }
199
        }
200
201
        return null;
202
    }
203
}
204