Skip to content

Instantly share code, notes, and snippets.

@akrez
Created August 12, 2024 12:29
Show Gist options
  • Save akrez/62b09416ce3886713092296c86e7c5cb to your computer and use it in GitHub Desktop.
Save akrez/62b09416ce3886713092296c86e7c5cb to your computer and use it in GitHub Desktop.
Reset Password Request in LARAVEL 11 with Mobile ( or SMS or Phone or anything else except Email )
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Rules\IranMobileRule;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Password;
use Illuminate\Validation\ValidationException;
class ForgotPasswordController extends Controller
{
/**
* Display the form to request a password reset link.
*
* @return \Illuminate\View\View
*/
public function showLinkRequestForm()
{
return view('auth.passwords.mobile');
}
/**
* Send a reset link to the given user.
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse
*/
public function sendResetLinkEmail(Request $request)
{
$this->validateMobile($request);
// We will send the password reset link to this user. Once we have attempted
// to send the link, we will examine the response then see the message we
// need to show to the user. Finally, we'll send out a proper response.
$response = $this->broker()->sendResetLink(
$this->credentials($request)
);
return $response == Password::RESET_LINK_SENT
? $this->sendResetLinkResponse($request, $response)
: $this->sendResetLinkFailedResponse($request, $response);
}
/**
* Validate the mobile for the given request.
*
* @return void
*/
protected function validateMobile(Request $request)
{
$request->validate(['mobile' => ['required', new IranMobileRule]]);
}
/**
* Get the needed authentication credentials from the request.
*
* @return array
*/
protected function credentials(Request $request)
{
return $request->only('mobile');
}
/**
* Get the response for a successful password reset link.
*
* @param string $response
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse
*/
protected function sendResetLinkResponse(Request $request, $response)
{
return $request->wantsJson()
? new JsonResponse(['message' => trans($response)], 200)
: back()->with('status', trans($response));
}
/**
* Get the response for a failed password reset link.
*
* @param string $response
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Illuminate\Validation\ValidationException
*/
protected function sendResetLinkFailedResponse(Request $request, $response)
{
if ($request->wantsJson()) {
throw ValidationException::withMessages([
'mobile' => [trans($response)],
]);
}
return back()
->withInput($request->only('mobile'))
->withErrors(['mobile' => trans($response)]);
}
/**
* Get the broker to be used during password reset.
*
* @return \Illuminate\Contracts\Auth\PasswordBroker
*/
public function broker()
{
return Password::broker();
}
}
<?php
namespace App\Auth;
use Illuminate\Auth\Passwords\DatabaseTokenRepository;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
use Illuminate\Support\Carbon;
use Illuminate\Support\Str;
class MobileDatabaseTokenRepository extends DatabaseTokenRepository
{
/**
* Create a new token record.
*
* @return string
*/
public function create(CanResetPasswordContract $user)
{
$mobile = $user->getEmailForPasswordReset();
$this->deleteExisting($user);
// We will create a new, random token for the user so that we can e-mail them
// a safe link to the password reset form. Then we will insert a record in
// the database so that we can verify the token within the actual reset.
$token = $this->createNewToken();
$this->getTable()->insert($this->getPayload($mobile, $token));
return $token;
}
/**
* Delete all existing reset tokens from the database.
*
* @return int
*/
protected function deleteExisting(CanResetPasswordContract $user)
{
return $this->getTable()->where('mobile', $user->getEmailForPasswordReset())->delete();
}
/**
* Build the record payload for the table.
*
* @param string $mobile
* @param string $token
* @return array
*/
protected function getPayload($mobile, #[\SensitiveParameter] $token)
{
return ['mobile' => $mobile, 'token' => $this->hasher->make($token), 'created_at' => new Carbon];
}
/**
* Determine if a token record exists and is valid.
*
* @param string $token
* @return bool
*/
public function exists(CanResetPasswordContract $user, #[\SensitiveParameter] $token)
{
$record = (array) $this->getTable()->where(
'mobile', $user->getEmailForPasswordReset()
)->first();
return $record &&
! $this->tokenExpired($record['created_at']) &&
$this->hasher->check($token, $record['token']);
}
/**
* Determine if the given user recently created a password reset token.
*
* @return bool
*/
public function recentlyCreatedToken(CanResetPasswordContract $user)
{
$record = (array) $this->getTable()->where(
'mobile', $user->getEmailForPasswordReset()
)->first();
return $record && $this->tokenRecentlyCreated($record['created_at']);
}
/**
* Create a new token for the user.
*
* @return string
*/
public function createNewToken()
{
return hash_hmac('sha256', Str::random(6), $this->hashKey);
}
}
<?php
namespace App\Auth;
use Illuminate\Auth\Passwords\PasswordBroker;
class MobilePasswordBroker extends PasswordBroker {}
<?php
namespace App\Auth;
use Illuminate\Auth\Passwords\PasswordBrokerManager;
use InvalidArgumentException;
/**
* @mixin \Illuminate\Contracts\Auth\PasswordBroker
*/
class MobilePasswordBrokerManager extends PasswordBrokerManager
{
/**
* Resolve the given broker.
*
* @param string $name
* @return \Illuminate\Contracts\Auth\PasswordBroker
*
* @throws \InvalidArgumentException
*/
protected function resolve($name)
{
$config = $this->getConfig($name);
if (is_null($config)) {
throw new InvalidArgumentException("Password resetter [{$name}] is not defined.");
}
// The password broker uses a token repository to validate tokens and send user
// password e-mails, as well as validating that password reset process as an
// aggregate service of sorts providing a convenient interface for resets.
return new MobilePasswordBroker(
$this->createTokenRepository($config),
$this->app['auth']->createUserProvider($config['provider'] ?? null),
$this->app['events'] ?? null,
);
}
/**
* Create a token repository instance based on the given configuration.
*
* @return \Illuminate\Auth\Passwords\TokenRepositoryInterface
*/
protected function createTokenRepository(array $config)
{
$key = $this->app['config']['app.key'];
if (str_starts_with($key, 'base64:')) {
$key = base64_decode(substr($key, 7));
}
$connection = $config['connection'] ?? null;
return new MobileDatabaseTokenRepository(
$this->app['db']->connection($connection),
$this->app['hash'],
$config['table'],
$key,
$config['expire'],
$config['throttle'] ?? 0
);
}
}
<?php
namespace App\Providers;
use App\Auth\MobilePasswordBrokerManager;
use Illuminate\Auth\Passwords\PasswordResetServiceProvider as PasswordResetServiceProviderBase;
class PasswordResetServiceProvider extends PasswordResetServiceProviderBase
{
protected function registerPasswordBroker()
{
$this->app->singleton('auth.password', function ($app) {
return new MobilePasswordBrokerManager($app);
});
$this->app->bind('auth.password.broker', function ($app) {
return $app->make('auth.password')->broker();
});
}
}
<?php
#bootstrap/providers.php
return [
App\Providers\AppServiceProvider::class,
App\Providers\PasswordResetServiceProvider::class,
];
@extends('layouts.auth')
@section('content')
@if (session('status'))
<div class="p-4 mb-4 text-sm text-green-800 rounded-lg bg-green-50 dark:bg-gray-800 dark:text-green-400"
role="alert">
{{ session('status') }}
</div>
@endif
<form class="w-full space-y-3" method="POST" action="{{ route('password.email') }}">
@csrf
<div class="grid sm:grid-cols-1 gap-4">
@include('auth._form', ['names' => ['mobile']])
</div>
@include('components.SubmitButton', ['content' => __('Send Password Reset Link')])
</form>
@endsection
<?php
namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use App\Enums\User\GenderEnum;
use App\Enums\User\RoleEnum;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Passport\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
// ...
/**
* Get the e-mail address where password reset links are sent.
*
* @return string
*/
public function getEmailForPasswordReset()
{
return $this->mobile;
}
/**
* Send the password reset notification.
*
* @param string $token
* @return void
*/
public function sendPasswordResetNotification(#[\SensitiveParameter] $token)
{
info($token);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment