I am using built in laravel auth functionality.Its working fine.I am trying to override following two functionality.
1.send forgot password email using mandrill.
2.send verification email while registering account.
Can any one help me to solve this issue
My aim is to use mandril instead of default email
I can see auth built in methods but i didnt got idea how i can override that
trait ResetsPasswords
{
use RedirectsUsers;
/**
* Display the password reset view for the given token.
*
* If no token is present, display the link request form.
*
* #param \Illuminate\Http\Request $request
* #param string|null $token
* #return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function showResetForm(Request $request, $token = null)
{
return view('auth.passwords.reset')->with(
['token' => $token, 'email' => $request->email]
);
}
/**
* Reset the given user's password.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function reset(Request $request)
{
$this->validate($request, $this->rules(), $this->validationErrorMessages());
// Here we will attempt to reset the user's password. If it is successful we
// will update the password on an actual user model and persist it to the
// database. Otherwise we will parse the error and return the response.
$response = $this->broker()->reset(
$this->credentials($request), function ($user, $password) {
$this->resetPassword($user, $password);
}
);
// If the password was successfully reset, we will redirect the user back to
// the application's home authenticated view. If there is an error we can
// redirect them back to where they came from with their error message.
return $response == Password::PASSWORD_RESET
? $this->sendResetResponse($response)
: $this->sendResetFailedResponse($request, $response);
}
As answered by Mahfuzal, Laravel comes with a bunch of mail drivers out of the box. So just update your .env file to use the right driver.
As for sending a verification email when creating an account, you just need to override the postRegister() function inside the Auth/AuthController like so:
public function postRegister(Request $request)
{
$validator = $this->validator($request->all());
if ($validator->fails()) {
$this->throwValidationException(
$request, $validator
);
}
$confirmation_code = str_random(30);
$newUser = new User;
$newUser->username = $request->username;
$newUser->email = $request->email;
$newUser->password = bcrypt($request->password);
$newUser->confirmation_code = $confirmation_code;
$newUser->save();
$data = array('confirmation_code' => $confirmation_code, 'username' => $request->username);
Mail::send('emails.verify', $data, function ($message) use ($newUser){
$message->to($newUser->email, $newUser->username);
$message->subject('Please verify your email address');
});
return redirect('/auth/login');
}
This will execute the above code when registering a user rather than what Laravel does default out of the box so just tweak it to your needs.
You then just need to create a function that will check the token and verify their account when they click the link. For that, I use something similar to what is explained here.
Laravel provides drivers for SMTP, Mailgun, Mandrill, Amazon SES,
PHP's mail function, and sendmail, allowing you to quickly get started
sending mail through a local or cloud based service of your choice.
Open your .env file and change following by your Mandrill credentials and then you're good to go.
MAIL_DRIVER=mandrill
MAIL_HOST=
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
You can create your own reset method in the controller that uses the trait to override the method in the trait.
Related
Currently the logic behind Resetting Password is that user must provide valid/registered e-mail to receive password recovery e-mail.
In my case I don't want to validate if the e-mail is registered or not due to security concerns and I want to just do the check in back-end and tell user that "If he has provided registered e-mail, he should get recovery e-mail shortly".
What I've done to achieve this is edited in vendor\laravel\framework\src\Illuminate\Auth\Passwords\PasswordBroker.php sendResetLink() method from this:
/**
* Send a password reset link to a user.
*
* #param array $credentials
* #return string
*/
public function sendResetLink(array $credentials)
{
// First we will check to see if we found a user at the given credentials and
// if we did not we will redirect back to this current URI with a piece of
// "flash" data in the session to indicate to the developers the errors.
$user = $this->getUser($credentials);
if (is_null($user)) {
return static::INVALID_USER;
}
// Once we have the reset token, we are ready to send the message out to this
// user with a link to reset their password. We will then redirect back to
// the current URI having nothing set in the session to indicate errors.
$user->sendPasswordResetNotification(
$this->tokens->create($user)
);
return static::RESET_LINK_SENT;
}
to this:
/**
* Send a password reset link to a user.
*
* #param array $credentials
* #return string
*/
public function sendResetLink(array $credentials)
{
// First we will check to see if we found a user at the given credentials and
// if we did not we will redirect back to this current URI with a piece of
// "flash" data in the session to indicate to the developers the errors.
$user = $this->getUser($credentials);
// if (is_null($user)) {
// return static::INVALID_USER;
// }
// Once we have the reset token, we are ready to send the message out to this
// user with a link to reset their password. We will then redirect back to
// the current URI having nothing set in the session to indicate errors.
if(!is_null($user)) {
$user->sendPasswordResetNotification(
$this->tokens->create($user)
);
}
return static::RESET_LINK_SENT;
}
This hard-coded option is not the best solution because it will disappear after update.. so I would like to know how can I extend or implement this change within the project scope within App folder to preserve this change at all times?
P.S. I've tried solution mentioned here: Laravel 5.3 Password Broker Customization but it didn't work.. also directory tree differs and I couldn't understand where to put new PasswordBroker.php file.
Thanks in advance!
Here are the steps you need to follow.
Create a new custom PasswordResetsServiceProvider. I have a folder (namespace) called Extensions where I'll place this file:
<?php
namespace App\Extensions\Passwords;
use Illuminate\Auth\Passwords\PasswordResetServiceProvider as BasePasswordResetServiceProvider;
class PasswordResetServiceProvider extends BasePasswordResetServiceProvider
{
/**
* Indicates if loading of the provider is deferred.
*
* #var bool
*/
protected $defer = true;
/**
* Register the service provider.
*
* #return void
*/
public function register()
{
$this->registerPasswordBroker();
}
/**
* Register the password broker instance.
*
* #return void
*/
protected function registerPasswordBroker()
{
$this->app->singleton('auth.password', function ($app) {
return new PasswordBrokerManager($app);
});
$this->app->bind('auth.password.broker', function ($app) {
return $app->make('auth.password')->broker();
});
}
}
As you can see this provider extends the base password reset provider. The only thing that changes is that we are returning a custom PasswordBrokerManager from the registerPasswordBroker method. Let's create a custom Broker manager in the same namespace:
<?php
namespace App\Extensions\Passwords;
use Illuminate\Auth\Passwords\PasswordBrokerManager as BasePasswordBrokerManager;
class PasswordBrokerManager extends BasePasswordBrokerManager
{
/**
* 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 PasswordBroker(
$this->createTokenRepository($config),
$this->app['auth']->createUserProvider($config['provider'] ?? null)
);
}
}
Again, this PasswordBrokerManager extends the base manager from laravel. The only difference here is the new resolve method which returns a new and custom PasswordBroker from the same namespace. So the last file we'll create a custom PasswordBroker in the same namespace:
<?php
namespace App\Extensions\Passwords;
use Illuminate\Auth\Passwords\PasswordBroker as BasePasswordBroker;
class PasswordBroker extends BasePasswordBroker
{
/**
* Send a password reset link to a user.
*
* #param array $credentials
* #return string
*/
public function sendResetLink(array $credentials)
{
// First we will check to see if we found a user at the given credentials and
// if we did not we will redirect back to this current URI with a piece of
// "flash" data in the session to indicate to the developers the errors.
$user = $this->getUser($credentials);
// if (is_null($user)) {
// return static::INVALID_USER;
// }
// Once we have the reset token, we are ready to send the message out to this
// user with a link to reset their password. We will then redirect back to
// the current URI having nothing set in the session to indicate errors.
if(!is_null($user)) {
$user->sendPasswordResetNotification(
$this->tokens->create($user)
);
}
return static::RESET_LINK_SENT;
}
}
As you can see we extend the default PasswordBroker class from Laravel and only override the method we need to override.
The final step is to simply replace the Laravel Default PasswordReset broker with ours. In the config/app.php file, change the line that registers the provider as such:
'providers' => [
...
// Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
App\Extensions\Passwords\PasswordResetServiceProvider::class,
...
]
That's all you need to register a custom password broker. Hope that helps.
The easiest solution here would be to place your customised code in app\Http\Controllers\Auth\ForgotPasswordController - this is the controller that pulls in the SendsPasswordResetEmails trait.
Your method overrides the one provided by that trait, so it will be called instead of the one in the trait. You could override the whole sendResetLinkEmail method with your code to always return the same response regardless of success.
public function sendResetLinkEmail(Request $request)
{
$this->validateEmail($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(
$request->only('email')
);
return back()->with('status', "If you've provided registered e-mail, you should get recovery e-mail shortly.");
}
You can just override the sendResetLinkFailedResponse method in your ForgetPasswordController class.
protected function sendResetLinkFailedResponse(Request $request, $response)
{
return $this->sendResetLinkResponse($request, Password::RESET_LINK_SENT);
}
We'll just send the successful response even if the validation failed.
I want to have register / login and send password reset on the same page.
I achieve to have register and login on the same page with different input name. But I don't find a way to add password reset input.
I want to call it "reset_email" but on my controller, if I try :
public function sendResetLinkEmail(Request $request)
{
$this->validateEmail($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(
$request->only('email')
);
return $response == Password::RESET_LINK_SENT
? $this->sendResetLinkResponse($response)
: $this->sendResetLinkFailedResponse($request, $response);
}
/**
* Validate the email for the given request.
*
* #param \Illuminate\Http\Request $request
* #return void
*/
protected function validateEmail(Request $request)
{
$this->validate($request, ['reset_email' => 'required|email']);
}
I've got this error :
We can't find a user with that e-mail address.
Any idea how to use reset_email instead of email for my input name ?
Thank for your help.
Update your method like this:
...
$response = $this->broker()->sendResetLink(
['email' => $request->get('reset_email')]
);
...
This will get your input value and will send it to the password broker with key email, so it will look for users by this column.
I have a laravel 5.4 installation and I always used the default Laravel Authentication guard to handle user authentication and, mainly, the password restore process.
Now I had to encrypt the email in the users table using the Elocryptfive library, so I also added email_hash field where the hash of the mail is stored in the db in order to easily retrieve users by their email.
I can easily authenticate users using the hash:
Auth::attempt([
'email_hash' => hash('sha256', $request->get('email')),
'password' => $request->get('password')]
, $remember);
What I can't get working is the password reset process. Is there a class to override in order to retrieve users by email_hash, then access the decrypted email and send the mail, without rewriting the whole password forgotten process?
I found a way to achieve this. I will answer my own question to provide a useful solution if someone else needs some help on the topic:
In your ForgotPasswordController.php, override the sendResetLinkEmail function:
/**
* Send a reset link to the given user.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\RedirectResponse
*/
public function sendResetLinkEmail(Request $request)
{
$this->validateEmail($request);
$hashed = hash('sha256', $request->get('email'));
$user = User::where('email_hash', $hashed)->first();
if (!is_null($user)) {
$response = Password::sendResetLink(
['email_hash' => $hashed]
);
} else {
$response = Password::INVALID_USER;
}
return $response == Password::RESET_LINK_SENT
? $this->sendResetLinkResponse($response)
: $this->sendResetLinkFailedResponse($request, $response);
}
In your ResetPasswordController.php, override the credentials function:
/**
* Get the password reset credentials from the request.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
protected function credentials(Request $request)
{
return [
'email_hash' => hash('sha256', $request->get('email')),
'password' => $request->get('password'),
'password_confirmation' => $request->get('password_confirmation'),
'token' => $request->get('token')
];
}
Thanks to Mike Rodham for pointing out the right direction, I hope it helps someone.
I used Dingo API package to create an api like Telegram api.
In the first step User sends some parameters , then I verify those and if all things was true user informations included a Token returned. like this :
public function signIn (Request $request)
{
$phone_number = $request->get('phone_number');
$phone_code_hash = $request->get('phone_code_hash');
$phone_code = $request->get('phone_code');
if ($this->verifyCode($phone_code_hash, $phone_code, $phone_number)) {
$user = User::where('phone_number', $phone_number)->firstOrFail();
$user->injectToken();
return $this->response->item($user, new UserTransformer);
} else {
return [
'success' => false,
'type' => 'PHONE_NUMBER_IS_NOT_REGISTERED',
'code' => 703,
'message' => 'Phone Code Is Correct But Phone Number Is Not Registered.'
];
}
}
As you can see signIn method has user authentication duty.
Now I do not know how can I listen to authentication events like Illuminate\Auth\Events\Authenticated ,Illuminate\Auth\Events\Login and etc described here.
In fact I want to run an event whenever user login was successfull.
I'm using larvel 5.3.
Any solution ؟
These events are not supported out of the box by Dingo.
You may however, create a custom middleware to fire an Illuminate\Auth\Events\Authenticated event on success like so:
app/Http/Middleware/Auth.php
namespace App\Http\Middleware;
use Dingo\Api\Http\Middleware\Auth as BaseAuth;
use Closure;
class Auth extends BaseAuth {
/**
* Perform authentication before a request is executed.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
*
* #return mixed
*/
public function handle($request, Closure $next)
{
$route = $this->router->getCurrentRoute();
if (! $this->auth->check(false)) {
$this->auth->authenticate($route->getAuthenticationProviders());
event(new \Illuminate\Auth\Events\Authenticated($this->auth->getUser());
}
return $next($request);
}
}
Finally, just register and add this middleware to your routes
I'm working with laravel Auth. Trying to add new rule with email and password, if status(field in user model) is = 1 then he cannot login. I cannot find where should i add this. I was looking at middleware, guard.php AuthenticateUsers.php but did not found it..
Edit:
I've solved this by creating new middleware that checks for this field. Also it can be done with Auth::attempt
You can try as:
if (Auth::attempt(['email' => $email, 'password' => $password, 'status' => 1])) {
// The user is active, not suspended, and exists.
}
From the Docs
If you wish, you also may add extra conditions to the authentication query in addition to the user's e-mail and password.
Add this code to your LoginController:
/**
* Attempt to log the user with custom credentials into the application.
*
* #param \Illuminate\Http\Request $request
* #return bool
*/
protected function attemptLogin(Request $request)
{
$credentials = $this->credentials($request);
$credentials['status'] = 1; // Additional field you want to check
return $this->guard()->attempt(
$credentials, $request->filled('remember')
);
}
Here we add the status field as a part of checked credentials. If user has status not equal to 1, authentication will fail.
Works with Laravel5.6