WebAuthnDeviceLostControllerTest.php 5.4 KB

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