WebAuthnDeviceLostControllerTest.php 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. <?php
  2. namespace Tests\Feature\Http\Auth;
  3. use App\Extensions\WebauthnCredentialBroker;
  4. use App\Http\Controllers\Auth\WebAuthnDeviceLostController;
  5. use App\Http\Requests\WebauthnDeviceLostRequest;
  6. use App\Models\User;
  7. use App\Notifications\WebauthnRecoveryNotification;
  8. use App\Providers\AuthServiceProvider;
  9. use Illuminate\Support\Facades\Lang;
  10. use Illuminate\Support\Facades\Notification;
  11. use PHPUnit\Framework\Attributes\CoversClass;
  12. use Tests\FeatureTestCase;
  13. /**
  14. * WebAuthnDeviceLostControllerTest test class
  15. */
  16. #[CoversClass(WebAuthnDeviceLostController::class)]
  17. #[CoversClass(WebauthnRecoveryNotification::class)]
  18. #[CoversClass(WebauthnCredentialBroker::class)]
  19. #[CoversClass(WebauthnDeviceLostRequest::class)]
  20. #[CoversClass(AuthServiceProvider::class)]
  21. class WebAuthnDeviceLostControllerTest extends FeatureTestCase
  22. {
  23. /**
  24. * @var \App\Models\User
  25. */
  26. protected $user;
  27. /**
  28. * @test
  29. */
  30. public function setUp() : void
  31. {
  32. parent::setUp();
  33. $this->user = User::factory()->create();
  34. }
  35. /**
  36. * @test
  37. *
  38. * @covers \App\Models\Traits\WebAuthnManageCredentials
  39. */
  40. public function test_sendRecoveryEmail_sends_notification_on_success()
  41. {
  42. Notification::fake();
  43. $response = $this->json('POST', '/webauthn/lost', [
  44. 'email' => $this->user->email,
  45. ]);
  46. Notification::assertSentTo($this->user, WebauthnRecoveryNotification::class);
  47. $response->assertStatus(200)
  48. ->assertJsonStructure([
  49. 'message',
  50. ]);
  51. $this->assertDatabaseHas(config('auth.passwords.webauthn.table'), [
  52. 'email' => $this->user->email,
  53. ]);
  54. }
  55. /**
  56. * @test
  57. */
  58. public function test_WebauthnRecoveryNotification_renders_to_email()
  59. {
  60. $mail = (new WebauthnRecoveryNotification('test_token'))->toMail($this->user)->render();
  61. $this->assertStringContainsString(
  62. 'http://localhost/webauthn/recover?token=test_token&amp;email=' . urlencode($this->user->email),
  63. $mail
  64. );
  65. $this->assertStringContainsString(
  66. Lang::get('Recover Account'),
  67. $mail
  68. );
  69. $this->assertStringContainsString(
  70. Lang::get(
  71. 'You are receiving this email because we received an account recovery request for your account.'
  72. ),
  73. $mail
  74. );
  75. $this->assertStringContainsString(
  76. Lang::get(
  77. 'This recovery link will expire in :count minutes.',
  78. ['count' => config('auth.passwords.webauthn.expire')]
  79. ),
  80. $mail
  81. );
  82. $this->assertStringContainsString(
  83. Lang::get('If you did not request an account recovery, no further action is required.'),
  84. $mail
  85. );
  86. }
  87. /**
  88. * @test
  89. */
  90. public function test_sendRecoveryEmail_does_not_send_anything_to_unknown_email()
  91. {
  92. Notification::fake();
  93. $response = $this->json('POST', '/webauthn/lost', [
  94. 'email' => 'bad@email.com',
  95. ]);
  96. Notification::assertNothingSent();
  97. $response->assertStatus(422)
  98. ->assertJsonValidationErrors([
  99. 'email',
  100. ]);
  101. $this->assertDatabaseMissing(config('auth.passwords.webauthn.table'), [
  102. 'email' => 'bad@email.com',
  103. ]);
  104. }
  105. /**
  106. * @test
  107. */
  108. public function test_sendRecoveryEmail_does_not_send_anything_to_invalid_email()
  109. {
  110. Notification::fake();
  111. $response = $this->json('POST', '/webauthn/lost', [
  112. 'email' => 'bad@email.com',
  113. ]);
  114. Notification::assertNothingSent();
  115. $response->assertStatus(422)
  116. ->assertJsonValidationErrors([
  117. 'email',
  118. ]);
  119. $this->assertDatabaseMissing(config('auth.passwords.webauthn.table'), [
  120. 'email' => 'bad@email.com',
  121. ]);
  122. }
  123. /**
  124. * @test
  125. */
  126. public function test_sendRecoveryEmail_does_not_send_anything_to_not_WebAuthnAuthenticatable()
  127. {
  128. $mock = $this->mock(\App\Extensions\WebauthnCredentialBroker::class)->makePartial();
  129. $mock->shouldReceive('getUser')
  130. ->andReturn(new \Illuminate\Foundation\Auth\User());
  131. Notification::fake();
  132. $response = $this->json('POST', '/webauthn/lost', [
  133. 'email' => $this->user->email,
  134. ]);
  135. Notification::assertNothingSent();
  136. $response->assertStatus(422)
  137. ->assertJsonValidationErrors([
  138. 'email',
  139. ]);
  140. }
  141. /**
  142. * @test
  143. */
  144. public function test_sendRecoveryEmail_is_throttled()
  145. {
  146. Notification::fake();
  147. $response = $this->json('POST', '/webauthn/lost', [
  148. 'email' => $this->user->email,
  149. ]);
  150. Notification::assertSentTo($this->user, WebauthnRecoveryNotification::class);
  151. $response->assertStatus(200)
  152. ->assertJsonStructure([
  153. 'message',
  154. ]);
  155. $this->assertDatabaseHas(config('auth.passwords.webauthn.table'), [
  156. 'email' => $this->user->email,
  157. ]);
  158. $this->json('POST', '/webauthn/lost', [
  159. 'email' => $this->user->email,
  160. ])
  161. ->assertStatus(422)
  162. ->assertJsonValidationErrorfor('email')
  163. ->assertJsonFragment([
  164. 'message' => __('passwords.throttled'),
  165. ]);
  166. }
  167. /**
  168. * @test
  169. */
  170. public function test_error_if_no_broker_is_set()
  171. {
  172. $this->app['config']->set('auth.passwords.webauthn', null);
  173. $this->json('POST', '/webauthn/lost', [
  174. 'email' => $this->user->email,
  175. ])
  176. ->assertStatus(500);
  177. }
  178. }