OAuthTwoTest.php 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. <?php
  2. namespace Laravel\Socialite\Tests;
  3. use Illuminate\Contracts\Session\Session;
  4. use Illuminate\Http\RedirectResponse;
  5. use Illuminate\Http\Request;
  6. use Illuminate\Support\Str;
  7. use Laravel\Socialite\Tests\Fixtures\FacebookTestProviderStub;
  8. use Laravel\Socialite\Tests\Fixtures\OAuthTwoTestProviderStub;
  9. use Laravel\Socialite\Tests\Fixtures\OAuthTwoWithPKCETestProviderStub;
  10. use Laravel\Socialite\Two\InvalidStateException;
  11. use Laravel\Socialite\Two\Token;
  12. use Laravel\Socialite\Two\User;
  13. use Mockery as m;
  14. use PHPUnit\Framework\TestCase;
  15. use stdClass;
  16. use Symfony\Component\HttpFoundation\RedirectResponse as SymfonyRedirectResponse;
  17. class OAuthTwoTest extends TestCase
  18. {
  19. protected function tearDown(): void
  20. {
  21. parent::tearDown();
  22. m::close();
  23. }
  24. public function testRedirectGeneratesTheProperIlluminateRedirectResponseWithoutPKCE()
  25. {
  26. $request = Request::create('foo');
  27. $request->setLaravelSession($session = m::mock(Session::class));
  28. $state = null;
  29. $closure = function ($name, $stateInput) use (&$state) {
  30. if ($name === 'state') {
  31. $state = $stateInput;
  32. return true;
  33. }
  34. return false;
  35. };
  36. $session->expects('put')->withArgs($closure);
  37. $provider = new OAuthTwoTestProviderStub($request, 'client_id', 'client_secret', 'redirect');
  38. $response = $provider->redirect();
  39. $this->assertInstanceOf(SymfonyRedirectResponse::class, $response);
  40. $this->assertInstanceOf(RedirectResponse::class, $response);
  41. $this->assertSame('http://auth.url?client_id=client_id&redirect_uri=redirect&scope=&response_type=code&state='.$state, $response->getTargetUrl());
  42. }
  43. private static $codeVerifier = null;
  44. public function testRedirectGeneratesTheProperIlluminateRedirectResponseWithPKCE()
  45. {
  46. $request = Request::create('foo');
  47. $request->setLaravelSession($session = m::mock(Session::class));
  48. $state = null;
  49. $sessionPutClosure = function ($name, $value) use (&$state) {
  50. if ($name === 'state') {
  51. $state = $value;
  52. return true;
  53. } elseif ($name === 'code_verifier') {
  54. self::$codeVerifier = $value;
  55. return true;
  56. }
  57. return false;
  58. };
  59. $sessionPullClosure = function ($name) {
  60. if ($name === 'code_verifier') {
  61. return self::$codeVerifier;
  62. }
  63. };
  64. $session->expects('put')->twice()->withArgs($sessionPutClosure);
  65. $session->expects('get')->with('code_verifier')->andReturnUsing($sessionPullClosure);
  66. $provider = new OAuthTwoWithPKCETestProviderStub($request, 'client_id', 'client_secret', 'redirect');
  67. $response = $provider->redirect();
  68. $codeChallenge = rtrim(strtr(base64_encode(hash('sha256', self::$codeVerifier, true)), '+/', '-_'), '=');
  69. $this->assertInstanceOf(SymfonyRedirectResponse::class, $response);
  70. $this->assertInstanceOf(RedirectResponse::class, $response);
  71. $this->assertSame('http://auth.url?client_id=client_id&redirect_uri=redirect&scope=&response_type=code&state='.$state.'&code_challenge='.$codeChallenge.'&code_challenge_method=S256', $response->getTargetUrl());
  72. }
  73. public function testTokenRequestIncludesPKCECodeVerifier()
  74. {
  75. $request = Request::create('foo', 'GET', ['state' => str_repeat('A', 40), 'code' => 'code']);
  76. $request->setLaravelSession($session = m::mock(Session::class));
  77. $codeVerifier = Str::random(32);
  78. $session->expects('pull')->with('state')->andReturns(str_repeat('A', 40));
  79. $session->expects('pull')->with('code_verifier')->andReturns($codeVerifier);
  80. $provider = new OAuthTwoWithPKCETestProviderStub($request, 'client_id', 'client_secret', 'redirect_uri');
  81. $provider->http = m::mock(stdClass::class);
  82. $provider->http->expects('post')->with('http://token.url', [
  83. 'headers' => ['Accept' => 'application/json'], 'form_params' => ['grant_type' => 'authorization_code', 'client_id' => 'client_id', 'client_secret' => 'client_secret', 'code' => 'code', 'redirect_uri' => 'redirect_uri', 'code_verifier' => $codeVerifier],
  84. ])->andReturns($response = m::mock(stdClass::class));
  85. $response->expects('getBody')->andReturns('{ "access_token" : "access_token", "refresh_token" : "refresh_token", "expires_in" : 3600 }');
  86. $user = $provider->user();
  87. $this->assertInstanceOf(User::class, $user);
  88. $this->assertSame('foo', $user->id);
  89. $this->assertSame('access_token', $user->token);
  90. $this->assertSame('refresh_token', $user->refreshToken);
  91. $this->assertSame(3600, $user->expiresIn);
  92. $this->assertSame($user->id, $provider->user()->id);
  93. }
  94. public function testUserReturnsAUserInstanceForTheAuthenticatedRequest()
  95. {
  96. $request = Request::create('foo', 'GET', ['state' => str_repeat('A', 40), 'code' => 'code']);
  97. $request->setLaravelSession($session = m::mock(Session::class));
  98. $session->expects('pull')->with('state')->andReturns(str_repeat('A', 40));
  99. $provider = new OAuthTwoTestProviderStub($request, 'client_id', 'client_secret', 'redirect_uri');
  100. $provider->http = m::mock(stdClass::class);
  101. $provider->http->expects('post')->with('http://token.url', [
  102. 'headers' => ['Accept' => 'application/json'], 'form_params' => ['grant_type' => 'authorization_code', 'client_id' => 'client_id', 'client_secret' => 'client_secret', 'code' => 'code', 'redirect_uri' => 'redirect_uri'],
  103. ])->andReturns($response = m::mock(stdClass::class));
  104. $response->expects('getBody')->andReturns('{ "access_token" : "access_token", "refresh_token" : "refresh_token", "expires_in" : 3600 }');
  105. $user = $provider->user();
  106. $this->assertInstanceOf(User::class, $user);
  107. $this->assertSame('foo', $user->id);
  108. $this->assertSame('access_token', $user->token);
  109. $this->assertSame('refresh_token', $user->refreshToken);
  110. $this->assertSame(3600, $user->expiresIn);
  111. $this->assertSame($user->id, $provider->user()->id);
  112. }
  113. public function testUserReturnsAUserInstanceForTheAuthenticatedFacebookRequest()
  114. {
  115. $request = Request::create('foo', 'GET', ['state' => str_repeat('A', 40), 'code' => 'code']);
  116. $request->setLaravelSession($session = m::mock(Session::class));
  117. $session->expects('pull')->with('state')->andReturns(str_repeat('A', 40));
  118. $provider = new FacebookTestProviderStub($request, 'client_id', 'client_secret', 'redirect_uri');
  119. $provider->http = m::mock(stdClass::class);
  120. $provider->http->expects('post')->with('https://graph.facebook.com/v3.3/oauth/access_token', [
  121. 'form_params' => ['grant_type' => 'authorization_code', 'client_id' => 'client_id', 'client_secret' => 'client_secret', 'code' => 'code', 'redirect_uri' => 'redirect_uri'],
  122. ])->andReturns($response = m::mock(stdClass::class));
  123. $response->expects('getBody')->andReturns(json_encode(['access_token' => 'access_token', 'expires' => 5183085]));
  124. $user = $provider->user();
  125. $this->assertInstanceOf(User::class, $user);
  126. $this->assertSame('foo', $user->id);
  127. $this->assertSame('access_token', $user->token);
  128. $this->assertNull($user->refreshToken);
  129. $this->assertSame(5183085, $user->expiresIn);
  130. $this->assertSame($user->id, $provider->user()->id);
  131. }
  132. public function testExceptionIsThrownIfStateIsInvalid()
  133. {
  134. $this->expectException(InvalidStateException::class);
  135. $request = Request::create('foo', 'GET', ['state' => str_repeat('B', 40), 'code' => 'code']);
  136. $request->setLaravelSession($session = m::mock(Session::class));
  137. $session->expects('pull')->with('state')->andReturns(str_repeat('A', 40));
  138. $provider = new OAuthTwoTestProviderStub($request, 'client_id', 'client_secret', 'redirect');
  139. $provider->user();
  140. }
  141. public function testExceptionIsThrownIfStateIsNotSet()
  142. {
  143. $this->expectException(InvalidStateException::class);
  144. $request = Request::create('foo', 'GET', ['state' => 'state', 'code' => 'code']);
  145. $request->setLaravelSession($session = m::mock(Session::class));
  146. $session->expects('pull')->with('state');
  147. $provider = new OAuthTwoTestProviderStub($request, 'client_id', 'client_secret', 'redirect');
  148. $provider->user();
  149. }
  150. public function testUserRefreshesToken()
  151. {
  152. $request = Request::create('/');
  153. $provider = new OAuthTwoTestProviderStub($request, 'client_id', 'client_secret', 'redirect_uri');
  154. $provider->http = m::mock(stdClass::class);
  155. $provider->http->expects('post')->with('http://token.url', [
  156. 'headers' => ['Accept' => 'application/json'],
  157. 'form_params' => ['grant_type' => 'refresh_token', 'client_id' => 'client_id', 'client_secret' => 'client_secret', 'refresh_token' => 'refresh_token'],
  158. ])->andReturns($response = m::mock(stdClass::class));
  159. $response->expects('getBody')->andReturns('{ "access_token" : "access_token", "refresh_token" : "refresh_token", "expires_in" : 3600, "scope" : "scope1,scope2" }');
  160. $token = $provider->refreshToken('refresh_token');
  161. $this->assertInstanceOf(Token::class, $token);
  162. $this->assertSame('access_token', $token->token);
  163. $this->assertSame('refresh_token', $token->refreshToken);
  164. $this->assertSame(3600, $token->expiresIn);
  165. $this->assertSame(['scope1', 'scope2'], $token->approvedScopes);
  166. }
  167. }