1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* OAuth 2.0 SSO grant |
4
|
|
|
* |
5
|
|
|
* @package league/oauth2-server |
6
|
|
|
* @author Luis Cordero H. ([email protected]) |
7
|
|
|
* @license http://mit-license.org/ |
8
|
|
|
* @link https://github.com/thephpleague/oauth2-server |
9
|
|
|
*/ |
10
|
|
|
|
11
|
|
|
namespace LucaDegasperi\OAuth2Server\Grant; |
12
|
|
|
|
13
|
|
|
use League\OAuth2\Server\Grant\AbstractGrant; |
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\Entity\SessionEntity; |
18
|
|
|
use League\OAuth2\Server\Event; |
19
|
|
|
use League\OAuth2\Server\Exception; |
20
|
|
|
use League\OAuth2\Server\Util\SecureKey; |
21
|
|
|
|
22
|
|
|
/** |
23
|
|
|
* Password grant class |
24
|
|
|
*/ |
25
|
|
|
class SSOGrant extends AbstractGrant |
26
|
|
|
{ |
27
|
|
|
/** |
28
|
|
|
* Define constants for the name of properties expected into the credentials. |
29
|
|
|
*/ |
30
|
|
|
const SSO_IDENTITY_FIELD = 'identity'; |
31
|
|
|
const SSO_REDIRECT_URI_FIELD = 'redirect_uri'; |
32
|
|
|
const SSO_SIGNATURE_FIELD = 'signature'; |
33
|
|
|
const SSO_CLIENT_FIELD = 'client_app'; |
34
|
|
|
const SSO_NONCE_FIELD = 'nonce'; |
35
|
|
|
const SSO_ORGANIZATION_FIELD = 'organization'; |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* Grant identifier |
39
|
|
|
* |
40
|
|
|
* @var string |
41
|
|
|
*/ |
42
|
|
|
protected $identifier = 'password'; |
43
|
|
|
|
44
|
|
|
/** |
45
|
|
|
* Response type |
46
|
|
|
* |
47
|
|
|
* @var string |
48
|
|
|
*/ |
49
|
|
|
protected $responseType; |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* Callback to authenticate a user's name and password |
53
|
|
|
* |
54
|
|
|
* @var callable |
55
|
|
|
*/ |
56
|
|
|
protected $callback; |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* Access token expires in override |
60
|
|
|
* |
61
|
|
|
* @var int |
62
|
|
|
*/ |
63
|
|
|
protected $accessTokenTTL; |
64
|
|
|
|
65
|
|
|
/** |
66
|
|
|
* Set the callback to verify a user's username and password |
67
|
|
|
* |
68
|
|
|
* @param callable $callback The callback function |
69
|
|
|
* |
70
|
|
|
* @return void |
71
|
|
|
*/ |
72
|
|
|
public function setVerifyCredentialsCallback(callable $callback) |
73
|
|
|
{ |
74
|
|
|
$this->callback = $callback; |
75
|
|
|
} |
76
|
|
|
|
77
|
|
|
/** |
78
|
|
|
* Return the callback function |
79
|
|
|
* |
80
|
|
|
* @return callable |
81
|
|
|
* |
82
|
|
|
* @throws |
83
|
|
|
*/ |
84
|
|
View Code Duplication |
protected function getVerifyCredentialsCallback() |
|
|
|
|
85
|
|
|
{ |
86
|
|
|
if (is_null($this->callback) || !is_callable($this->callback)) |
87
|
|
|
{ |
88
|
|
|
throw new Exception\ServerErrorException('Null or non-callable callback set on SSO grant'); |
89
|
|
|
} |
90
|
|
|
|
91
|
|
|
return $this->callback; |
92
|
|
|
} |
93
|
|
|
|
94
|
|
|
/** |
95
|
|
|
* Complete the password grant |
96
|
|
|
* |
97
|
|
|
* @return array |
98
|
|
|
* |
99
|
|
|
* @throws |
100
|
|
|
*/ |
101
|
|
|
public function completeFlow() |
102
|
|
|
{ |
103
|
|
|
// Get the required params |
104
|
|
|
$clientId = $this->server->getRequest()->request->get('client_id', $this->server->getRequest()->getUser()); |
105
|
|
|
if (is_null($clientId)) { |
106
|
|
|
throw new Exception\InvalidRequestException('client_id'); |
107
|
|
|
} |
108
|
|
|
|
109
|
|
|
$clientSecret = $this->server->getRequest()->request->get('client_secret', |
110
|
|
|
$this->server->getRequest()->getPassword()); |
111
|
|
|
if (is_null($clientSecret)) { |
112
|
|
|
throw new Exception\InvalidRequestException('client_secret'); |
113
|
|
|
} |
114
|
|
|
|
115
|
|
|
$identity = $this->server->getRequest()->request->get(self::SSO_IDENTITY_FIELD, null); |
116
|
|
|
if (is_null($identity)) |
117
|
|
|
{ |
118
|
|
|
throw new Exception\InvalidRequestException('identity'); |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
$redirect_uri = $this->server->getRequest()->request->get(self::SSO_REDIRECT_URI_FIELD, null); |
122
|
|
|
if ( is_null($redirect_uri) ) |
123
|
|
|
{ |
124
|
|
|
throw new Exception\InvalidRequestException('redirect_uri'); |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
$signature = $this->server->getRequest()->request->get(self::SSO_SIGNATURE_FIELD, null); |
128
|
|
|
if ( is_null($signature) ) |
129
|
|
|
{ |
130
|
|
|
throw new Exception\InvalidRequestException('signature'); |
131
|
|
|
} |
132
|
|
|
|
133
|
|
|
$client_app = $this->server->getRequest()->request->get(self::SSO_CLIENT_FIELD, null); |
134
|
|
|
if ( is_null($client_app) ) |
135
|
|
|
{ |
136
|
|
|
throw new Exception\InvalidRequestException('client_app'); |
137
|
|
|
} |
138
|
|
|
|
139
|
|
|
$nonce = $this->server->getRequest()->request->get(self::SSO_NONCE_FIELD, null); |
140
|
|
|
if ( is_null($nonce) ) |
141
|
|
|
{ |
142
|
|
|
throw new Exception\InvalidRequestException('nonce'); |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
$organization = $this->server->getRequest()->request->get(self::SSO_ORGANIZATION_FIELD, null); |
146
|
|
|
if ( is_null($organization) ) |
147
|
|
|
{ |
148
|
|
|
throw new Exception\InvalidRequestException('organization'); |
149
|
|
|
} |
150
|
|
|
|
151
|
|
|
// Validate client ID and client secret |
152
|
|
|
$client = $this->server->getClientStorage()->get( |
153
|
|
|
$clientId, |
154
|
|
|
$clientSecret, |
155
|
|
|
null, |
156
|
|
|
$this->getIdentifier() |
157
|
|
|
); |
158
|
|
|
|
159
|
|
View Code Duplication |
if (($client instanceof ClientEntity) === false) { |
|
|
|
|
160
|
|
|
$this->server->getEventEmitter()->emit(new Event\ClientAuthenticationFailedEvent($this->server->getRequest())); |
|
|
|
|
161
|
|
|
throw new Exception\InvalidClientException(); |
162
|
|
|
} |
163
|
|
|
|
164
|
|
|
$credentials = [ |
165
|
|
|
self::SSO_IDENTITY_FIELD => $identity, |
166
|
|
|
self::SSO_REDIRECT_URI_FIELD => $redirect_uri, |
167
|
|
|
self::SSO_SIGNATURE_FIELD => $signature, |
168
|
|
|
self::SSO_CLIENT_FIELD => $client_app, |
169
|
|
|
self::SSO_NONCE_FIELD => $nonce, |
170
|
|
|
self::SSO_ORGANIZATION_FIELD => $organization |
171
|
|
|
]; |
172
|
|
|
|
173
|
|
|
// Check if user's username and password are correct |
174
|
|
|
$userId = call_user_func($this->getVerifyCredentialsCallback(), $credentials ); |
175
|
|
|
|
176
|
|
|
if ($userId === false) { |
177
|
|
|
$this->server->getEventEmitter()->emit(new Event\UserAuthenticationFailedEvent($this->server->getRequest())); |
|
|
|
|
178
|
|
|
throw new Exception\InvalidCredentialsException(); |
179
|
|
|
} |
180
|
|
|
|
181
|
|
|
// Validate any scopes that are in the request |
182
|
|
|
$scopeParam = $this->server->getRequest()->request->get('scope', ''); |
183
|
|
|
$scopes = $this->validateScopes($scopeParam, $client); |
184
|
|
|
|
185
|
|
|
// Create a new session |
186
|
|
|
$session = new SessionEntity($this->server); |
187
|
|
|
$session->setOwner('user', $userId); |
188
|
|
|
$session->associateClient($client); |
189
|
|
|
|
190
|
|
|
// Generate an access token |
191
|
|
|
$accessToken = new AccessTokenEntity($this->server); |
192
|
|
|
$accessToken->setId(SecureKey::generate()); |
193
|
|
|
$accessToken->setExpireTime($this->getAccessTokenTTL() + time()); |
194
|
|
|
|
195
|
|
|
// Associate scopes with the session and access token |
196
|
|
|
foreach ($scopes as $scope) { |
197
|
|
|
$session->associateScope($scope); |
198
|
|
|
} |
199
|
|
|
|
200
|
|
|
foreach ($session->getScopes() as $scope) { |
201
|
|
|
$accessToken->associateScope($scope); |
202
|
|
|
} |
203
|
|
|
|
204
|
|
|
$this->server->getTokenType()->setSession($session); |
205
|
|
|
$this->server->getTokenType()->setParam('access_token', $accessToken->getId()); |
206
|
|
|
$this->server->getTokenType()->setParam('expires_in', $this->getAccessTokenTTL()); |
207
|
|
|
|
208
|
|
|
// Associate a refresh token if set |
209
|
|
|
if ($this->server->hasGrantType('refresh_token')) { |
210
|
|
|
$refreshToken = new RefreshTokenEntity($this->server); |
211
|
|
|
$refreshToken->setId(SecureKey::generate()); |
212
|
|
|
$refreshToken->setExpireTime($this->server->getGrantType('refresh_token')->getRefreshTokenTTL() + time()); |
|
|
|
|
213
|
|
|
$this->server->getTokenType()->setParam('refresh_token', $refreshToken->getId()); |
214
|
|
|
} |
215
|
|
|
|
216
|
|
|
// Save everything |
217
|
|
|
$session->save(); |
218
|
|
|
$accessToken->setSession($session); |
219
|
|
|
$accessToken->save(); |
220
|
|
|
|
221
|
|
|
if ($this->server->hasGrantType('refresh_token')) { |
222
|
|
|
$refreshToken->setAccessToken($accessToken); |
|
|
|
|
223
|
|
|
$refreshToken->save(); |
224
|
|
|
} |
225
|
|
|
|
226
|
|
|
return $this->server->getTokenType()->generateResponse(); |
227
|
|
|
} |
228
|
|
|
} |
229
|
|
|
|
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.