Skip to content

Commit

Permalink
Prevent InvalidStateException being thrown if user method is called m…
Browse files Browse the repository at this point in the history
…ore than once (#204)
  • Loading branch information
pjotrsavitski authored Jan 23, 2022
1 parent dab81a0 commit 4e63afb
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 4 deletions.
19 changes: 15 additions & 4 deletions src/OAuth2/AbstractProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ abstract class AbstractProvider extends BaseProvider implements ProviderInterfac
*/
protected $credentialsResponseBody;

/**
* The cached user instance.
*
* @var \SocialiteProviders\Manager\OAuth2\User|null
*/
protected $user;

/**
* @param string $providerName
* @return string
Expand All @@ -34,22 +41,26 @@ public static function serviceContainerKey($providerName)
*/
public function user()
{
if ($this->user) {
return $this->user;
}

if ($this->hasInvalidState()) {
throw new InvalidStateException();
}

$response = $this->getAccessTokenResponse($this->getCode());
$this->credentialsResponseBody = $response;

$user = $this->mapUserToObject($this->getUserByToken(
$this->user = $this->mapUserToObject($this->getUserByToken(
$token = $this->parseAccessToken($response)
));

if ($user instanceof User) {
$user->setAccessTokenResponseBody($this->credentialsResponseBody);
if ($this->user instanceof User) {
$this->user->setAccessTokenResponseBody($this->credentialsResponseBody);
}

return $user->setToken($token)
return $this->user->setToken($token)
->setRefreshToken($this->parseRefreshToken($response))
->setExpiresIn($this->parseExpiresIn($response));
}
Expand Down
55 changes: 55 additions & 0 deletions tests/OAuthTwoTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -221,4 +221,59 @@ public function exceptionIsThrownIfStateIsNotSet(): void
$provider = new OAuthTwoTestProviderStub($request, 'client_id', 'client_secret', 'redirect');
$provider->user();
}

/**
* @test
*/
public function userObjectShouldBeCachedOnFirstCall(): void
{
$session = m::mock(SessionInterface::class);
$accessTokenResponseBody = '{"access_token": "access_token", "test": "test"}';
$request = Request::create('foo', 'GET', [
'state' => str_repeat('A', 40),
'code' => 'code',
]);
$request->setSession($session);
$session
->shouldReceive('pull')
->once()
->with('state')
->andReturn(str_repeat('A', 40));
$provider = new OAuthTwoTestProviderStub($request, 'client_id', 'client_secret', 'redirect_uri');

$provider->http = m::mock(stdClass::class);
$provider->http
->shouldReceive('post')
->once()
->with('http://token.url', [
'headers' => [
'Accept' => 'application/json',
],
'form_params' => [
'grant_type' => 'authorization_code',
'client_id' => 'client_id',
'client_secret' => 'client_secret',
'code' => 'code',
'redirect_uri' => 'redirect_uri',
],
])
->andReturn($response = m::mock(stdClass::class));
$response
->shouldReceive('getBody')
->andReturn($accessTokenResponseBody);

$reflection = new \ReflectionClass($provider);
$reflectionProperty = $reflection->getProperty('user');
$reflectionProperty->setAccessible(true);

$this->assertNull($reflectionProperty->getValue($provider));

$firstCall = $provider->user();

$this->assertInstanceOf(SocialiteOAuth2User::class, $reflectionProperty->getValue($provider));

$secondCall = $provider->user();

$this->assertSame($firstCall, $secondCall);
}
}

0 comments on commit 4e63afb

Please sign in to comment.