Completed
Pull Request — master (#774)
by
unknown
12:17
created

RefreshTokenGrant::completeFlow()   D

Complexity

Conditions 14
Paths 16

Size

Total Lines 130
Code Lines 64

Duplication

Lines 4
Ratio 3.08 %

Importance

Changes 0
Metric Value
dl 4
loc 130
rs 4.9516
c 0
b 0
f 0
cc 14
eloc 64
nc 16
nop 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * OAuth 2.0 Refresh token grant
4
 *
5
 * @package     league/oauth2-server
6
 * @author      Luis Diego Cordero Hernández <[email protected]>
7
 * @copyright   Copyright (c) Aristamd.com
8
 * @license     http://mit-license.org/
9
 * @link        https://github.com/aristamd/oauth2-server-laravel
10
 */
11
12
namespace LucaDegasperi\OAuth2Server\Grant;
13
14
use League\OAuth2\Server\Entity\AccessTokenEntity;
15
use League\OAuth2\Server\Entity\ClientEntity;
16
use League\OAuth2\Server\Entity\RefreshTokenEntity;
17
use League\OAuth2\Server\Event;
18
use League\OAuth2\Server\Exception;
19
use League\OAuth2\Server\Util\SecureKey;
20
use League\OAuth2\Server\Grant\RefreshTokenGrant as RefreshTokenGrantOriginal;
21
22
/**
23
 * Refresh token grant
24
 * 
25
 * This class creates a new Refresh token and asign it to the user session. Per
26
 * session we are going to have only one refresh token and with it you can
27
 * create as many access tokens as required. Every time that you access the Api
28
 * the expiration time of the refresh token will be delay by 45 minutes(depending on configuration)
29
 * The expiration fo the Access Token is always the same and can't be extended,
30
 * once it experices you will need to create a new one using the refresh token.
31
 * If the refresh token has expired then you won't be able to create a new access
32
 * token and because of that you will be redirected to the login page.
33
 */
34
class RefreshTokenGrant extends RefreshTokenGrantOriginal
35
{
36
    /**
37
     * {@inheritdoc}
38
     */
39
    public function completeFlow()
40
    {
41
        // Get clientid from request
42
        $clientId = $this->server->getRequest()->request->get('client_id', $this->server->getRequest()->getUser());
43
        if (is_null($clientId)) {
44
            throw new Exception\InvalidRequestException('client_id');
45
        }
46
47
        // Get client secret from request
48
        $clientSecret = $this->server->getRequest()->request->get('client_secret',
49
            $this->server->getRequest()->getPassword());
50
        if ($this->shouldRequireClientSecret() && is_null($clientSecret)) {
51
            throw new Exception\InvalidRequestException('client_secret');
52
        }
53
54
        // Validate client ID and client secret and return the client.
55
        $client = $this->server->getClientStorage()->get(
56
            $clientId,
57
            $clientSecret,
58
            null,
59
            $this->getIdentifier()
60
        );
61
62 View Code Duplication
        if (($client instanceof ClientEntity) === false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
63
            $this->server->getEventEmitter()->emit(new Event\ClientAuthenticationFailedEvent($this->server->getRequest()));
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 123 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
64
            throw new Exception\InvalidClientException();
65
        }
66
67
        // Gets the active refresh token for the current session.
68
        $oldRefreshTokenParam = $this->server->getRequest()->request->get('refresh_token', null);
69
        if ($oldRefreshTokenParam === null) {
70
            throw new Exception\InvalidRequestException('refresh_token');
71
        }
72
73
        // Validate refresh token
74
        $oldRefreshToken = $this->server->getRefreshTokenStorage()->get($oldRefreshTokenParam);
75
76
        if (($oldRefreshToken instanceof RefreshTokenEntity) === false) {
77
            throw new Exception\InvalidRefreshException();
78
        }
79
        
80
        if ($oldRefreshToken->getExpireTime() < time()) {
81
            throw new Exception\InvalidRefreshException();
82
        }
83
        
84
        // Using the expiration time of the current refresh token calculates the 
85
        // reminding time for the new access token.
86
        $expireTime = $oldRefreshToken->getExpireTime();
87
        $remindingTime = $expireTime - time();
88
89
        // Ensure the old refresh token hasn't expired
90
        if ($oldRefreshToken->isExpired() === true) {
91
            throw new Exception\InvalidRefreshException();
92
        }
93
94
        // It gets the previous access token.
95
        $oldAccessToken = $oldRefreshToken->getAccessToken();
96
97
        // Get the scopes for the original session
98
        $session = $oldAccessToken->getSession();
99
        $scopes = $this->formatScopes($session->getScopes());
100
101
        // Get and validate any requested scopes
102
        $requestedScopesString = $this->server->getRequest()->request->get('scope', '');
103
        $requestedScopes = $this->validateScopes($requestedScopesString, $client);
104
105
        // If no new scopes are requested then give the access token the original session scopes
106
        if (count($requestedScopes) === 0) {
107
            $newScopes = $scopes;
108
        } else {
109
            // The OAuth spec says that a refreshed access token can have the original scopes or fewer so ensure
110
            //  the request doesn't include any new scopes
111
            foreach ($requestedScopes as $requestedScope) {
112
                if (!isset($scopes[$requestedScope->getId()])) {
113
                    throw new Exception\InvalidScopeException($requestedScope->getId());
114
                }
115
            }
116
117
            $newScopes = $requestedScopes;
118
        }
119
120
        // Generate a new access token and assign it to the right sessions
121
        $newAccessToken = new AccessTokenEntity($this->server);
122
        $newAccessToken->setId(SecureKey::generate());
123
        $newAccessToken->setExpireTime($expireTime);
124
        $newAccessToken->setSession($session);
125
126
        foreach ($newScopes as $newScope) {
127
            $newAccessToken->associateScope($newScope);
128
        }
129
130
        // Save the new access token that is going to be returned to the user.
131
        $newAccessToken->save();
132
133
        // We need to assign the new access token to the current user session.
134
        // The exipiration time should be reminding time on the current refresh token
135
        // in that way event if you get a new access token a 10 seconds before the 
136
        // refresh token expires the new access token will have only 10 seconds left.
137
        $this->server->getTokenType()->setSession($session);
138
        $this->server->getTokenType()->setParam('access_token', $newAccessToken->getId());
139
        $this->server->getTokenType()->setParam('expires_in', $remindingTime);
140
141
        // Now we need to change the refresh token before exipiring the old access token,
142
        // otherwise it will delete the refresh token because of the delete cascade constraint 
143
        if ($this->shouldRotateRefreshTokens()) {
144
            // Expire the old refresh token
145
            $oldRefreshToken->expire();
146
147
            // Generate a new refresh token
148
            $newRefreshToken = new RefreshTokenEntity($this->server);
149
            $newRefreshToken->setId(SecureKey::generate());
150
            $newRefreshToken->setExpireTime($expireTime);
151
            $newRefreshToken->setAccessToken($newAccessToken);
152
            $newRefreshToken->save();
153
154
            $this->server->getTokenType()->setParam('refresh_token', $newRefreshToken->getId());
155
        } else {
156
            // Here you will change the refresh token and removes the reference to the old access token
157
            // and set the reference to the new access token.
158
            $oldRefreshToken->setAccessToken($newAccessToken);
159
            $oldRefreshToken->save();
160
            $this->server->getTokenType()->setParam('refresh_token', $oldRefreshToken->getId());
161
        }
162
163
        // Now its save to expire the old token, in that way it won't be available anymore.
164
        $oldAccessToken->expire();
165
        
166
        // Return a response with the tokens details
167
        return $this->server->getTokenType()->generateResponse();
168
    }
169
}