|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace Sludio\HelperBundle\Oauth\Client; |
|
4
|
|
|
|
|
5
|
|
|
use League\OAuth2\Client\Token\AccessToken; |
|
6
|
|
|
use LogicException; |
|
7
|
|
|
use Sludio\HelperBundle\Logger\SludioLogger; |
|
8
|
|
|
use Sludio\HelperBundle\Oauth\Exception\InvalidStateException; |
|
9
|
|
|
use Sludio\HelperBundle\Oauth\Exception\MissingAuthorizationCodeException; |
|
10
|
|
|
use Symfony\Component\HttpFoundation\RedirectResponse; |
|
11
|
|
|
use Symfony\Component\HttpFoundation\RequestStack; |
|
12
|
|
|
|
|
13
|
|
|
class OAuth2Client |
|
14
|
|
|
{ |
|
15
|
|
|
const OAUTH2_SESSION_STATE_KEY = 'sludio_helper.oauth_client_state'; |
|
16
|
|
|
|
|
17
|
|
|
protected $provider; |
|
18
|
|
|
protected $requestStack; |
|
19
|
|
|
protected $isStateless = true; |
|
20
|
|
|
protected $logger; |
|
21
|
|
|
|
|
22
|
|
|
public function __construct($provider, RequestStack $requestStack, SludioLogger $logger) |
|
23
|
|
|
{ |
|
24
|
|
|
$this->provider = $provider; |
|
25
|
|
|
$this->requestStack = $requestStack; |
|
26
|
|
|
$this->logger = $logger; |
|
27
|
|
|
} |
|
28
|
|
|
|
|
29
|
|
|
public function setAsStateless() |
|
30
|
|
|
{ |
|
31
|
|
|
$this->isStateless = true; |
|
32
|
|
|
} |
|
33
|
|
|
|
|
34
|
|
View Code Duplication |
public function redirect(array $scopes = [], array $options = [], $token = null) |
|
|
|
|
|
|
35
|
|
|
{ |
|
36
|
|
|
if (!empty($scopes)) { |
|
37
|
|
|
$options['scope'] = $scopes; |
|
38
|
|
|
} |
|
39
|
|
|
|
|
40
|
|
|
if ($token) { |
|
41
|
|
|
$options['token'] = $token; |
|
42
|
|
|
} |
|
43
|
|
|
|
|
44
|
|
|
$url = $this->provider->getAuthorizationUrl($options); |
|
45
|
|
|
|
|
46
|
|
|
if (!$this->isStateless) { |
|
47
|
|
|
$this->getSession()->set(self::OAUTH2_SESSION_STATE_KEY, $this->provider->getState()); |
|
48
|
|
|
} |
|
49
|
|
|
|
|
50
|
|
|
return new RedirectResponse($url); |
|
51
|
|
|
} |
|
52
|
|
|
|
|
53
|
|
|
public function getAccessToken(array $attributes = []) |
|
54
|
|
|
{ |
|
55
|
|
View Code Duplication |
if (!$this->isStateless) { |
|
|
|
|
|
|
56
|
|
|
$expectedState = $this->getSession()->get(self::OAUTH2_SESSION_STATE_KEY); |
|
57
|
|
|
$actualState = $this->getCurrentRequest()->query->get('state'); |
|
58
|
|
|
if (!$actualState || ($actualState !== $expectedState)) { |
|
59
|
|
|
$this->logger->error(__CLASS__.' ('.__LINE__.'): '.'Invalid state: '.var_export(var_export($actualState, 1).var_export($expectedState, 1), 1), 401); |
|
|
|
|
|
|
60
|
|
|
throw new InvalidStateException('error_oauth_invalid_state'); |
|
61
|
|
|
} |
|
62
|
|
|
} |
|
63
|
|
|
|
|
64
|
|
|
$code = $this->getCurrentRequest()->get('code'); |
|
65
|
|
|
|
|
66
|
|
|
if (!$code) { |
|
67
|
|
|
$this->logger->error(__CLASS__.' ('.__LINE__.'): '.'No "code" parameter was found!', 401); |
|
68
|
|
|
throw new MissingAuthorizationCodeException('error_oauth_code_parameter_not_found'); |
|
69
|
|
|
} |
|
70
|
|
|
|
|
71
|
|
|
return $this->provider->getAccessToken('authorization_code', [ |
|
72
|
|
|
'code' => $code, |
|
73
|
|
|
], $attributes); |
|
74
|
|
|
} |
|
75
|
|
|
|
|
76
|
|
|
public function fetchUserFromToken(AccessToken $accessToken) |
|
77
|
|
|
{ |
|
78
|
|
|
return $this->provider->getResourceOwner($accessToken); |
|
79
|
|
|
} |
|
80
|
|
|
|
|
81
|
|
|
public function fetchUser(array $attributes = []) |
|
82
|
|
|
{ |
|
83
|
|
|
$token = $this->getAccessToken($attributes); |
|
84
|
|
|
|
|
85
|
|
|
return $this->fetchUserFromToken($token); |
|
86
|
|
|
} |
|
87
|
|
|
|
|
88
|
|
|
public function getOAuth2Provider() |
|
89
|
|
|
{ |
|
90
|
|
|
return $this->provider; |
|
91
|
|
|
} |
|
92
|
|
|
|
|
93
|
|
|
protected function getCurrentRequest() |
|
94
|
|
|
{ |
|
95
|
|
|
$request = $this->requestStack->getCurrentRequest(); |
|
96
|
|
|
|
|
97
|
|
|
if (!$request) { |
|
98
|
|
|
$this->logger->error(__CLASS__.' ('.__LINE__.'): '.'There is no "current request", and it is needed to perform this action', 400); |
|
99
|
|
|
throw new LogicException('error_oauth_current_request_not_found'); |
|
100
|
|
|
} |
|
101
|
|
|
|
|
102
|
|
|
return $request; |
|
103
|
|
|
} |
|
104
|
|
|
|
|
105
|
|
|
protected function getSession() |
|
106
|
|
|
{ |
|
107
|
|
|
$session = $this->getCurrentRequest()->getSession(); |
|
108
|
|
|
|
|
109
|
|
|
if (!$session) { |
|
110
|
|
|
$this->logger->error(__CLASS__.' ('.__LINE__.'): '.'In order to use "state", you must have a session. Set the OAuth2Client to stateless to avoid stat$e', 400); |
|
111
|
|
|
throw new LogicException('error_oauth_session_not_found'); |
|
112
|
|
|
} |
|
113
|
|
|
|
|
114
|
|
|
return $session; |
|
115
|
|
|
} |
|
116
|
|
|
} |
|
117
|
|
|
|
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.