WebAuthnRecoveryController.php 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. <?php
  2. namespace App\Http\Controllers\Auth;
  3. use App\Extensions\WebauthnCredentialBroker;
  4. use App\Facades\Settings;
  5. use App\Http\Controllers\Controller;
  6. use App\Http\Requests\WebauthnRecoveryRequest;
  7. use Illuminate\Auth\AuthenticationException;
  8. use Illuminate\Foundation\Auth\ResetsPasswords;
  9. use Illuminate\Http\JsonResponse;
  10. use Illuminate\Http\Request;
  11. use Illuminate\Support\Facades\Auth;
  12. use Illuminate\Support\Facades\Password;
  13. use Illuminate\Validation\ValidationException;
  14. class WebAuthnRecoveryController extends Controller
  15. {
  16. use ResetsPasswords;
  17. /**
  18. * Let the user regain access to his account using email+password by resetting
  19. * the "use webauthn only" setting.
  20. *
  21. * @param \App\Http\Requests\WebauthnRecoveryRequest $request
  22. * @param \App\Extensions\WebauthnCredentialBroker $broker
  23. * @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse
  24. *
  25. * @throws \Illuminate\Validation\ValidationException
  26. */
  27. public function recover(WebauthnRecoveryRequest $request, WebauthnCredentialBroker $broker)
  28. {
  29. $credentials = $request->validated();
  30. $response = $broker->reset(
  31. $credentials,
  32. function ($user) use ($request) {
  33. // At this time, the WebAuthnUserProvider is already registered in the Laravel Service Container,
  34. // with a password_fallback value set using the useWebauthnOnly user setting (see AuthServiceProvider.php).
  35. // To ensure user login with email+pwd credentials, we replace the registered WebAuthnUserProvider instance
  36. // with a new instance configured with password_fallback On.
  37. $provider = new \Laragear\WebAuthn\Auth\WebAuthnUserProvider(
  38. app()->make('hash'),
  39. \App\Models\User::class,
  40. app()->make(\Laragear\WebAuthn\Assertion\Validator\AssertionValidator::class),
  41. true,
  42. );
  43. Auth::guard()->setProvider($provider);
  44. if (Auth::attempt($request->only('email', 'password'))) {
  45. if ($this->shouldRevokeAllCredentials($request)) {
  46. $user->flushCredentials();
  47. }
  48. Settings::delete('useWebauthnOnly');
  49. } else {
  50. throw new AuthenticationException();
  51. }
  52. }
  53. );
  54. return $response === Password::PASSWORD_RESET
  55. ? $this->sendRecoveryResponse($request, $response)
  56. : $this->sendRecoveryFailedResponse($request, $response);
  57. }
  58. /**
  59. * Check if the user has set to revoke all credentials.
  60. *
  61. * @param \App\Http\Requests\WebauthnRecoveryRequest $request
  62. * @return bool|mixed
  63. */
  64. protected function shouldRevokeAllCredentials(WebauthnRecoveryRequest $request) : mixed
  65. {
  66. return filter_var($request->header('WebAuthn-Unique'), FILTER_VALIDATE_BOOLEAN)
  67. ?: $request->input('revokeAll', true);
  68. }
  69. /**
  70. * Get the response for a successful account recovery.
  71. *
  72. * @param \Illuminate\Http\Request $request
  73. * @param string $response
  74. * @return \Illuminate\Http\JsonResponse
  75. */
  76. protected function sendRecoveryResponse(Request $request, string $response) : JsonResponse
  77. {
  78. return response()->json(['message' => __('auth.webauthn.webauthn_login_disabled')]);
  79. }
  80. /**
  81. * Get the response for a failed account recovery.
  82. *
  83. * @param \Illuminate\Http\Request $request
  84. * @param string $response
  85. * @return \Illuminate\Http\JsonResponse
  86. *
  87. * @throws \Illuminate\Validation\ValidationException
  88. */
  89. protected function sendRecoveryFailedResponse(Request $request, string $response) : JsonResponse
  90. {
  91. switch ($response) {
  92. case Password::INVALID_TOKEN:
  93. throw ValidationException::withMessages(['token' => [__('auth.webauthn.invalid_reset_token')]]);
  94. default:
  95. throw ValidationException::withMessages(['email' => [trans($response)]]);
  96. }
  97. }
  98. }