WebAuthnRecoveryController.php 4.1 KB

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