LoginTest.php 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. <?php
  2. namespace Tests\Feature\Http\Auth;
  3. use App\Facades\Settings;
  4. use App\Models\User;
  5. use Tests\FeatureTestCase;
  6. use Illuminate\Support\Carbon;
  7. /**
  8. * @covers \App\Http\Controllers\Auth\LoginController
  9. * @covers \App\Http\Middleware\RejectIfAuthenticated
  10. * @covers \App\Http\Middleware\RejectIfReverseProxy
  11. * @covers \App\Http\Middleware\RejectIfDemoMode
  12. * @covers \App\Http\Middleware\SkipIfAuthenticated
  13. */
  14. class LoginTest extends FeatureTestCase
  15. {
  16. /**
  17. * @var \App\Models\User
  18. */
  19. protected $user;
  20. private const PASSWORD = 'password';
  21. private const WRONG_PASSWORD = 'wrong_password';
  22. /**
  23. * @test
  24. */
  25. public function setUp(): void
  26. {
  27. parent::setUp();
  28. $this->user = User::factory()->create();
  29. }
  30. /**
  31. * @test
  32. */
  33. public function test_user_login_returns_success()
  34. {
  35. $response = $this->json('POST', '/user/login', [
  36. 'email' => $this->user->email,
  37. 'password' => self::PASSWORD,
  38. ])
  39. ->assertOk()
  40. ->assertExactJson([
  41. 'message' => 'authenticated',
  42. 'name' => $this->user->name,
  43. ]);
  44. }
  45. /**
  46. * @test
  47. *
  48. * @covers \App\Rules\CaseInsensitiveEmailExists
  49. */
  50. public function test_user_login_with_uppercased_email_returns_success()
  51. {
  52. $response = $this->json('POST', '/user/login', [
  53. 'email' => strtoupper($this->user->email),
  54. 'password' => self::PASSWORD,
  55. ])
  56. ->assertOk()
  57. ->assertExactJson([
  58. 'message' => 'authenticated',
  59. 'name' => $this->user->name,
  60. ]);
  61. }
  62. /**
  63. * @test
  64. *
  65. * @covers \App\Http\Middleware\SkipIfAuthenticated
  66. */
  67. public function test_user_login_already_authenticated_returns_bad_request()
  68. {
  69. $response = $this->json('POST', '/user/login', [
  70. 'email' => $this->user->email,
  71. 'password' => self::PASSWORD,
  72. ]);
  73. $response = $this->actingAs($this->user, 'web-guard')
  74. ->json('POST', '/user/login', [
  75. 'email' => $this->user->email,
  76. 'password' => self::PASSWORD,
  77. ])
  78. ->assertStatus(200)
  79. ->assertJson([
  80. 'message' => 'authenticated',
  81. 'name' => $this->user->name,
  82. ]);
  83. }
  84. /**
  85. * @test
  86. */
  87. public function test_user_login_with_missing_data_returns_validation_error()
  88. {
  89. $response = $this->json('POST', '/user/login', [
  90. 'email' => '',
  91. 'password' => '',
  92. ])
  93. ->assertStatus(422)
  94. ->assertJsonValidationErrors([
  95. 'email',
  96. 'password',
  97. ]);
  98. }
  99. /**
  100. * @test
  101. *
  102. * @covers \App\Exceptions\Handler
  103. */
  104. public function test_user_login_with_invalid_credentials_returns_authentication_error()
  105. {
  106. $response = $this->json('POST', '/user/login', [
  107. 'email' => $this->user->email,
  108. 'password' => self::WRONG_PASSWORD,
  109. ])
  110. ->assertStatus(401)
  111. ->assertJson([
  112. 'message' => 'unauthorised',
  113. ]);
  114. }
  115. /**
  116. * @test
  117. */
  118. public function test_too_many_login_attempts_with_invalid_credentials_returns_too_many_request_error()
  119. {
  120. $response = $this->json('POST', '/user/login', [
  121. 'email' => $this->user->email,
  122. 'password' => self::WRONG_PASSWORD,
  123. ]);
  124. $response = $this->json('POST', '/user/login', [
  125. 'email' => $this->user->email,
  126. 'password' => self::WRONG_PASSWORD,
  127. ]);
  128. $response = $this->json('POST', '/user/login', [
  129. 'email' => $this->user->email,
  130. 'password' => self::WRONG_PASSWORD,
  131. ]);
  132. $response = $this->json('POST', '/user/login', [
  133. 'email' => $this->user->email,
  134. 'password' => self::WRONG_PASSWORD,
  135. ]);
  136. $response = $this->json('POST', '/user/login', [
  137. 'email' => $this->user->email,
  138. 'password' => self::WRONG_PASSWORD,
  139. ]);
  140. $response = $this->json('POST', '/user/login', [
  141. 'email' => $this->user->email,
  142. 'password' => self::WRONG_PASSWORD,
  143. ]);
  144. $response->assertStatus(429);
  145. }
  146. /**
  147. * @test
  148. */
  149. public function test_user_logout_returns_validation_success()
  150. {
  151. $response = $this->json('POST', '/user/login', [
  152. 'email' => $this->user->email,
  153. 'password' => self::PASSWORD,
  154. ]);
  155. $response = $this->actingAs($this->user, 'web-guard')
  156. ->json('GET', '/user/logout')
  157. ->assertOk()
  158. ->assertExactJson([
  159. 'message' => 'signed out',
  160. ]);
  161. }
  162. /**
  163. * @test
  164. *
  165. * @covers \App\Http\Middleware\KickOutInactiveUser
  166. * @covers \App\Http\Middleware\LogUserLastSeen
  167. */
  168. public function test_user_logout_after_inactivity_returns_teapot()
  169. {
  170. // Set the autolock period to 1 minute
  171. Settings::set('kickUserAfter', 1);
  172. $response = $this->json('POST', '/user/login', [
  173. 'email' => $this->user->email,
  174. 'password' => self::PASSWORD,
  175. ]);
  176. // Ping a protected endpoint to log last_seen_at time
  177. $response = $this->actingAs($this->user, 'api-guard')
  178. ->json('GET', '/api/v1/twofaccounts');
  179. $this->travelTo(Carbon::now()->addMinutes(2));
  180. $response = $this->actingAs($this->user, 'api-guard')
  181. ->json('GET', '/api/v1/twofaccounts')
  182. ->assertStatus(418);
  183. }
  184. }