2018-06-05 23:00:01 -07:00
< ? php
2024-03-12 22:39:16 -04:00
namespace App\Http\Controllers\Api\Client ;
2018-06-05 23:00:01 -07:00
2024-03-12 22:39:16 -04:00
use App\Facades\Activity ;
use App\Http\Requests\Api\Client\Account\UpdateEmailRequest ;
use App\Http\Requests\Api\Client\Account\UpdatePasswordRequest ;
2025-11-03 08:31:07 +01:00
use App\Http\Requests\Api\Client\Account\UpdateUsernameRequest ;
2025-09-24 13:34:19 +02:00
use App\Services\Users\UserUpdateService ;
2024-06-23 16:33:18 +02:00
use App\Transformers\Api\Client\UserTransformer ;
2025-09-24 13:34:19 +02:00
use Illuminate\Auth\AuthManager ;
use Illuminate\Auth\SessionGuard ;
use Illuminate\Http\JsonResponse ;
use Illuminate\Http\Request ;
use Illuminate\Http\Response ;
2026-04-20 17:25:54 +02:00
use Illuminate\Support\Facades\RateLimiter ;
use Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException ;
2025-09-24 13:34:19 +02:00
use Throwable ;
2018-06-05 23:00:01 -07:00
class AccountController extends ClientApiController
{
2026-04-20 17:25:54 +02:00
/**
* The number of seconds that must elapse before the email change throttle resets .
*/
private const EMAIL_UPDATE_THROTTLE = 60 * 60 * 24 ;
2018-06-11 22:56:57 -07:00
/**
* AccountController constructor .
*/
2022-10-14 10:59:20 -06:00
public function __construct ( private AuthManager $manager , private UserUpdateService $updateService )
2018-06-11 22:56:57 -07:00
{
parent :: __construct ();
}
2025-02-26 16:12:19 +01:00
/**
* View account
2025-03-03 14:41:19 -05:00
*
* @ return array < array - key , mixed >
2025-02-26 16:12:19 +01:00
*/
2018-06-05 23:00:01 -07:00
public function index ( Request $request ) : array
{
return $this -> fractal -> item ( $request -> user ())
2024-06-23 16:33:18 +02:00
-> transformWith ( $this -> getTransformer ( UserTransformer :: class ))
2018-06-05 23:00:01 -07:00
-> toArray ();
}
2018-06-11 22:56:57 -07:00
2025-11-03 08:31:07 +01:00
/**
* Update username
*
* Update the authenticated user ' s username .
*/
public function updateUsername ( UpdateUsernameRequest $request ) : JsonResponse
{
$original = $request -> user () -> username ;
$this -> updateService -> handle ( $request -> user (), $request -> validated ());
if ( $original !== $request -> input ( 'username' )) {
Activity :: event ( 'user:account.username-changed' )
-> property ([ 'old' => $original , 'new' => $request -> input ( 'username' )])
-> log ();
}
return new JsonResponse ([], Response :: HTTP_NO_CONTENT );
}
2018-06-11 22:56:57 -07:00
/**
2025-02-26 16:12:19 +01:00
* Update email
*
2018-06-17 16:53:24 -07:00
* Update the authenticated user ' s email address .
2018-06-11 22:56:57 -07:00
*/
2020-06-25 22:12:09 -07:00
public function updateEmail ( UpdateEmailRequest $request ) : JsonResponse
2018-06-11 22:56:57 -07:00
{
2026-04-20 17:25:54 +02:00
$user = $request -> user ();
// Only allow a user to change their email three times in the span
// of 24 hours. This prevents malicious users from trying to find
// existing accounts in the system by constantly changing their email.
2026-07-09 09:55:11 -04:00
throw_if ( RateLimiter :: tooManyAttempts ( $key = " user:update-email: { $user -> uuid } " , 3 ), new TooManyRequestsHttpException ( message : 'Your email address has been changed too many times today. Please try again later.' ));
2026-04-20 17:25:54 +02:00
$original = $user -> email ;
if ( mb_strtolower ( $original ) !== mb_strtolower ( $request -> validated ( 'email' ))) {
RateLimiter :: hit ( $key , self :: EMAIL_UPDATE_THROTTLE );
$this -> updateService -> handle ( $user , $request -> validated ());
2018-06-11 22:56:57 -07:00
2022-07-03 14:29:01 -04:00
Activity :: event ( 'user:account.email-changed' )
-> property ([ 'old' => $original , 'new' => $request -> input ( 'email' )])
-> log ();
}
2022-05-29 18:48:35 -04:00
2020-06-25 22:12:09 -07:00
return new JsonResponse ([], Response :: HTTP_NO_CONTENT );
2018-06-17 16:53:24 -07:00
}
/**
2025-02-26 16:12:19 +01:00
* Update password
*
2019-12-28 12:07:42 -08:00
* Update the authenticated user ' s password . All existing sessions will be logged
* out immediately .
2018-06-17 16:53:24 -07:00
*
2025-09-08 13:12:33 -04:00
* @ throws Throwable
2018-06-17 16:53:24 -07:00
*/
2020-06-25 22:12:09 -07:00
public function updatePassword ( UpdatePasswordRequest $request ) : JsonResponse
2018-06-17 16:53:24 -07:00
{
2026-04-20 17:25:54 +02:00
$user = Activity :: event ( 'user:account.password-changed' ) -> transaction ( function () use ( $request ) {
return $this -> updateService -> handle ( $request -> user (), $request -> validated ());
});
2021-08-15 17:37:12 -07:00
2022-05-22 16:05:58 -04:00
$guard = $this -> manager -> guard ();
2021-08-15 17:37:12 -07:00
// If you do not update the user in the session you'll end up working with a
// cached copy of the user that does not include the updated password. Do this
// to correctly store the new user details in the guard and allow the logout
// other devices functionality to work.
2022-05-22 16:05:58 -04:00
$guard -> setUser ( $user );
2018-06-17 16:53:24 -07:00
2025-01-16 14:53:50 -05:00
if ( $guard instanceof SessionGuard ) {
2022-05-22 16:05:58 -04:00
$guard -> logoutOtherDevices ( $request -> input ( 'password' ));
}
2019-12-28 12:07:42 -08:00
2020-06-25 22:12:09 -07:00
return new JsonResponse ([], Response :: HTTP_NO_CONTENT );
2018-06-11 22:56:57 -07:00
}
2018-06-05 23:00:01 -07:00
}