I have altered my authController.php file to do a few things needed for my project. It works great, just need to make one more change. Right now the controller looks like this:
<?php
namespace App\Http\Controllers\Auth;
use App\User;
use App\Role;
use Mail;
use Validator;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ThrottlesLogins;
use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;
use Illuminate\Http\Request;
use Illuminate\Foundation\Auth\ResetsPasswords;
class AuthController extends Controller
{
/*
|--------------------------------------------------------------------------
| Registration & Login Controller
|--------------------------------------------------------------------------
|
| This controller handles the registration of new users, as well as the
| authentication of existing users. By default, this controller uses
| a simple trait to add these behaviors. Why don't you explore it?
|
*/
use AuthenticatesAndRegistersUsers, ThrottlesLogins, ResetsPasswords;
/**
* Where to redirect users after login / registration.
*
* #var string
*/
protected $redirectTo = '/add';
/**
* Create a new authentication controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware($this->guestMiddleware(), ['except' => 'logout']);
}
/**
* Overwrite the Laravel 5.2 standard registration
* so user will not be logged in automatically.
*
* #param array $request
* #return Register
*/
public function register(Request $request)
{
$validator = $this->validator($request->all());
if ($validator->fails()) {
$this->throwValidationException(
$request, $validator
);
}
$this->create($request->all());
return redirect($this->redirectPath());
}
/**
* Extend password reset email to user for when registering
*/
public function sendResetLinkEmail(Request $request)
{
$this->validateSendResetLinkEmail($request);
$broker = $this->getBroker();
$this->subject = "First Time User Setup";
$broker->emailView = "auth.emails.password";
$response = Password::broker($broker)->sendFirstTimeSetup(
$this->getSendResetLinkEmailCredentials($request),
$this->resetEmailBuilder()
);
switch ($response) {
case Password::RESET_LINK_SENT:
return $this->getSendResetLinkEmailSuccessResponse($response);
case Password::FIRST_TIME_SETUP:
return $this->getSendFirstTimeSetupEmailSuccessResponse($response);
case Password::INVALID_USER:
default:
return $this->getSendResetLinkEmailFailureResponse($response);
}
}
public function getSendFirstTimeSetupEmailSuccessResponse($response)
{
return redirect()->back()->with('status', trans($response));
}
/**
* Get a validator for an incoming registration request.
*
* #param array $data
* #return \Illuminate\Contracts\Validation\Validator
*/
protected function validator(array $data)
{
return Validator::make($data, [
'first-name' => 'required|max:255',
'last-name' => 'required|max:255',
'phone' => 'required|max:255',
'form' => 'max:255',
'email' => 'required|email|max:255|unique:users',
'password' => 'required|min:6|confirmed',
]);
}
/**
* Create a new user instance after a valid registration.
*
* #param array $data
* #return User
*/
protected function create(Request $request, array $data )
{
//Create the user
$user = User::create([
'first_name' => $data['first-name'],
'last_name' => $data['last-name'],
'phone' => $data['phone'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
]);
return $this->postEmail($request);
/*
Mail::send('auth.emails.registered', ['user' => $user], function ($m) use ($user)
{
$m->to($user->email, $user->first_name)->subject('You Have Been Added');
});*/
//Is it a User? Then give them that role
if ($data['user-role'] == 'user')
{
$role = Role::where('name', '=', 'user')->firstOrFail();
$user = User::find($user->id);
$user->roles()->attach($role->id);
}
//Is it an Admin? Then give them that role
if ($data['user-role'] == 'admin')
{
$role = Role::where('name', '=', 'owner')->firstOrFail();
$user = User::find($user->id);
$user->roles()->attach($role->id);
}
return $user;
}
}
It stops auto login on user creation, assigns a role to the user based on the form and sends a password reset email. The trouble is not I get the error Trait method guestMiddleware has not been applied, because there are collisions with other trait methods on App\Http\Controllers\Auth\AuthController
One of the tricks to this is understanding how the traits inside of the Auth factory work.
First, we'll need to use the use Illuminate\Foundation\Auth\ResetsPasswords; Trait as well as the AuthenticatesAndRegistersUsers and the ThrottlesLogins trait.
use AuthenticatesAndRegistersUsers, ThrottlesLogins, ResetsPassword;
Next, we need to make sure that we have a $request instance being passed to our create method:
protected function create(Illuminate\Http\Request $request, array $data)
Sidenote - I would recommend moving away from passing in a $data object as an argument and instead working with $request. You can get your data like this: $request->get('first-name'), etc.
Finally, we pass our $request to the postEmail() function:
return $this->postEmail($request);
This will pipe the $request to the ResetsPasswords\postEmail function:
public function postEmail(Request $request)
{
return $this->sendResetLinkEmail($request);
}
Which will inturn pipe it to the ResetsPasswords\sendResetLinkEmail function:
public function sendResetLinkEmail(Request $request)
{
$this->validateSendResetLinkEmail($request);
$broker = $this->getBroker();
$response = Password::broker($broker)->sendResetLink(
$this->getSendResetLinkEmailCredentials($request),
$this->resetEmailBuilder()
);
switch ($response) {
case Password::RESET_LINK_SENT:
return $this->getSendResetLinkEmailSuccessResponse($response);
case Password::INVALID_USER:
default:
return $this->getSendResetLinkEmailFailureResponse($response);
}
}
Which will ultimately send out an email.
The key to making this entire thing work is that the instance of Illuminate\Http\Request always contains an email field.
Note
The response that will be returned from the sendResetLinkEmail function may not be the same response that you sent out. Perhaps Users are confused by a Password Reset request when they've just created their account. Instead, maybe you want to send a First Time Setup. In order to do this, you will need to create your own Password Broker and Password Facade.
Next, lets make 2 new directories:
App\Brokers
App\Facades
Next, make a new file inside of App\Facades and call it Password.php. Namespace the file accordingly and extend the existing Password Facade. Also, we'll add another const as an observable response type for our FIRST_TIME_SETUP.
<?php
namespace App\Facades;
class Password extends \Illuminate\Support\Facades\Password {
/**
* Constant representing a successfully sent reminder.
*
* #var string
*/
const FIRST_TIME_SETUP = 'passwords.first_time_setup';
}
Now we have added another response type that we can switch on which will help dictate how we send out our email.
Next, we need to make our Password Broker and establish our sendFirstTimeSetup functionality.
namespace App\Brokers;
class PasswordBroker extends Illuminate\Auth\Passwords\PasswordBroker {
public function sendFirstTimeSetup(array $credentials, Closure $callback = null)
{
// 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.
$token = $this->tokens->create($user);
$this->emailResetLink($user, $token, $callback);
return static::FIRST_TIME_SETUP;
}
}
Now we need to copy the sendResetLinkEmail function that we saw earlier, and move it into our AuthController. This will allow us to modify the Password Facade that we use, and modify our switch statement to support our First Time Setup
public function sendResetLinkEmail(Request $request)
{
$this->validateSendResetLinkEmail($request);
$broker = $this->getBroker();
$response = Password::broker($broker)->sendFirstTimeSetup(
$this->getSendResetLinkEmailCredentials($request),
$this->resetEmailBuilder()
);
switch ($response) {
case Password::RESET_LINK_SENT:
return $this->getSendResetLinkEmailSuccessResponse($response);
case Password::FIRST_TIME_SETUP:
return $this->getSendFirstTimeSetupEmailSuccessResponse($response);
case Password::INVALID_USER:
default:
return $this->getSendResetLinkEmailFailureResponse($response);
}
}
Then, also in the AuthController, we'll make another function to return our response:
public function getSendFirstTimeSetupEmailSuccessResponse($response)
{
return redirect()->back()->with('status', trans($response));
}
Finally, if you want to override the view that is used by the function when being sent out, simply override the $broker->emailView property before invoking the ->sendFirstTimeSetup() function:
//...
$broker->emailView = "emails.user.first_time_setup";
$response = Password::broker($broker)->sendFirstTimeSetup(
$this->getSendResetLinkEmailCredentials($request),
$this->resetEmailBuilder()
);
//...
If you want to change the subject of the email, override the ->subject() property inside of your AuthController prior to firing your ->sendFirstTimeSetup() function:
//...
$this->subject = "First Time User Setup";
$broker->emailView = "emails.user.first_time_setup";
$response = Password::broker($broker)->sendFirstTimeSetup(
$this->getSendResetLinkEmailCredentials($request),
$this->resetEmailBuilder()
);
//...
I could go on and on, but I think this will get you going in the right direction. Hopefully it will help a few others as well.
You can fix your trait collision issue by changing the use block to the following:
use ThrottlesLogins,
ResetsPasswords,
AuthenticatesAndRegistersUsers {
AuthenticatesAndRegistersUsers::guestMiddleware insteadof ResetsPasswords;
AuthenticatesAndRegistersUsers::getGuard insteadof ResetsPasswords;
AuthenticatesAndRegistersUsers::redirectPath insteadof ResetsPasswords;
}
It's ugly, but it will fix all related collisions, thus allowing you to just use the pre-built Illuminate methods if you so choose.
Related
I want to send an SMS to a mobile phone (if he had already turned on the two-factor authentication system).
So at LoginController I added this method:
protected function authenticated(Request $request, $user)
{
return $this->loggendin($request , $user);
}
And this loggendin method is inside of a trait called TwoFactorAuthentication, which goes like this:
trait TwoFactorAuthenticate
{
public function loggendin(Request $request , $user)
{
if($user->hasTwoFactorAuthenticatedEnabled()) {
auth()->logout();
$request->session()->flash('auth' , [
'user_id' => $user->id,
'using_sms' => false,
'remember' => $request->has('remember')
]);
if($user->two_factor_type == 'sms') {
$code = ActiveCode::generateCode($user);
// Todo Send Sms
$request->user()->notify(new ActiveCodeNotification($code , $user->phone_number));
$request->session()->push('auth.using_sms' , true);
}
return redirect(route('twofa.token'));
}
return false;
}
}
Now the problem is when I want to log in, this message appears on the screen which is saying:
Error Call to a member function notify() on null
Which is referring to this line:
$request->user()->notify(new ActiveCodeNotification($code , $user->phone_number));
And this ActiveCodeNotification holds some settings for sending the SMS.
If you would like to visit that, here it is:
class ActiveCodeNotification extends Notification
{
use Queueable;
public $code;
public $phoneNumber;
/**
* Create a new notification instance.
*
* #return void
*/
public function __construct($code , $phoneNumber)
{
$this->code = $code;
$this->phoneNumber = $phoneNumber;
}
/**
* Get the notification's delivery channels.
*
* #param mixed $notifiable
* #return array
*/
public function via($notifiable)
{
return [GhasedakChannel::class];
}
public function toGhasedakSms($notifiable)
{
return [
'text' => "{$this->code}",
'number' => $this->phoneNumber
];
}
}
So what's going wrong here that I get Call to a member function notify() on null while it's two parameters have value.
So if you know, please let me know. I would really appreciate any idea from you guys...
Thanks.
Try this:
First, make sure your User model has the Notifiable trait.
Top of the User Model class:
use Illuminate\Notifications\Notifiable;
After that:
class User extends Model{
use Notifiable; // ....
And then...
Instead of
$request->user()->notify(new ActiveCodeNotification($code , $user->phone_number));
Use this
$user->notify(new ActiveCodeNotification($code, $user->phone_number));
Or
Before calling auth()->logout();
use it at first:
auth()->user()->notify(new ActiveCodeNotification($code, $user->phone_number));
then, you can call auth()->logout();
Worked for me recently
The $request->user() would be null at that point on LoginController because the request was not authenticated and the user is not set on the request instance yet.
You have to use the $user argument instead:
$user->notify(new ActiveCodeNotification($code , $user->phone_number));
Note: make sure that your User model has Illuminate\Notifications\Notifiable trait in order to be able to use notify().
Invoice app development is going on using Laravel. I store date and amount format for every users in settings table.
When user login to their account how to set Session variable? Please give any suggestions. I am using Laravel 5.3.
Of course the docs tell us how to store session data*, but they don't address the OP's question regarding storing session data at login. You have a couple options but I think the clearest way is to override the AuthenticatesUsers trait's authenticated method.
Add the override to your LoginController:
/**
* The user has been authenticated.
*
* #param \Illuminate\Http\Request $request
* #param mixed $user
* #return mixed
*/
protected function authenticated(Request $request, $user)
{
$this->setUserSession($user);
}
Then you can set your session up as:
protected function setUserSession($user)
{
session(
[
'last_invoiced_at' => $user->settings->last_invoiced_at,
'total_amount_due' => $user->settings->total_amount_due
]
);
}
If you want to be a bit more clever you can create a listener for the Login or Authenticated events and set up the session when one of those events* fires.
Create a listener such as SetUpUserSession:
<?php
namespace app\Listeners;
use Illuminate\Auth\Events\Login;
class SetUserSession
{
/**
* #param Login $event
* #return void
*/
public function handle(Login $event)
{
session(
[
'last_invoiced_at' => $event->user->settings->last_invoiced_at,
'total_amount_due' => $event->user->settings->total_amount_due
]
);
}
}
*Links go to 5.4 but this hasn't changed from 5.3.
I've used the Auth class to manage user data, like this:
public function index(){
$user_id = Auth::user()->id;
}
But you have to add 'use Auth;' before class declaration. Then you can add any data to session variable.
Laravel fires an event when a new login is made to the application.
When an event fires you may add a listener for it, then add a session .
This is the content of a listener I made.
<?php
namespace App\Listeners\Auth;
use Illuminate\Auth\Events\Login;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
class UserLoggedIn
{
/**
* Create the event listener.
*
* #return void
*/
public function __construct()
{
//
}
public function handle(Login $event)
{
if ($event->user->hasRole('subsidiary_admin')) {
\Session::put('subsidiary_admin', $event->user->subsidiaryBoUser->subsidiary_id);
\Session::put('subsidiary', $event->user->subsidiaryBoUser->subsidiary);
}
}
}
and I register it on the eventServiceProvider like this
'Illuminate\Auth\Events\Login' => [
'App\Listeners\Auth\UserLoggedIn',
],
You can store data in the session using two different methods either a Request instance or using the global helper/function provided.
Request Instance
public function methodA(Request $request) {
$request->session()->put('KEY', 'VALUE');
}
Global Helper
public function methodB() {
session(['key' => 'value']);
}
You can find more details on both methods in the documentation.
Here's what I am doing:
I have this on my helper file:
\App\Helpers\helpers.php:
function signedUser()
{
return [
'id' => Auth::id(),
'group_id' => Auth::user()->group_id,
'group_name' => Auth::user()->group->name,
'avatar' => Auth::user()->avatar,
'first_name' => Auth::user()->first_name,
'full_name' => Auth::user()->full_name,
];
}
On my User Model:
public function group()
{
return $this->belongsTo('App\Models\Group');
}
public function getFullNameAttribute()
{
$full_name = ucfirst($this->first_name) . ' ' . ucfirst($this->middle_name[0]) . '. ' . ucfirst($this->last_name);
return $full_name;
}
Then I can accessed the variables on both controllers and blade files like so:
dump(signedUser()['full_name']);
{{ signedUser()['full_name'] }}
I have a form that submits to a controller, which validates the data. If the validation fails it redirects back with the input and the errors. This is the method that deals with the form submission:
<?php namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\User;
class UserController extends Controller {
/**
* Create a new user.
*
* #param Reqeust $request
*
* #return Void
*/
public function postCreate(Request $request)
{
$user = new User;
$rules = $user->rules();
$rules['password'] = 'required|confirmed|min:8';
$v = \Validator::make($request->except('_token', 'roles'), $rules);
if ($v->fails())
{
return redirect()->back()->withInput($request->except('_token', 'password', 'password_confirmation'))->withErrors($v);
}
$user->fill($request->except('_token', 'password', 'password_confirmation'));
$user->password = \Hash::make($request->input('password'));
$user->save();
return redirect()->route('webmanAccounts')->with('messages', [['text' => 'User account created', 'class' => 'alert-success']]);
}
On the page that displays the form I check to see if name, one of the fields, is present and if so populate a User object with the data. The problem is input is always empty.
<?php namespace BackEnd;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Request as RequestFacade;
use App\Http\Controllers\Controller;
use App\Models\Role;
use App\Models\User;
class UserController extends Controller {
public function __construct(Request $request)
{
if ( ! $request->user()->can('accounts'))
{
return abort(403, 'You do not have permission to access this page.');
}
}
/**
* Display the create new user form and process any error messages.
*
* #param Reqeust $request
*
* #return View
*/
public function create(Request $request)
{
$user = new User;
dump(RequestFacade::all());
if (RequestFacade::has('name'))
{
$user->fill(RequestFacade::except('_token', 'roles'));
foreach (RequestFacade::only('roles') as $role)
{
$user->roles()->add($role);
}
}
return view('backend.user.create', ['title' => 'Website Manager :: Create New Account', 'user' => $user, 'roles' => Role::all()]);
}
I have tried RequestFacade, $request and Input, all show as empty. Why isn't the data being passed back?
To add to the strangeness of this, I have another project that uses almost identical code and that works perfectly fine. Why would it work fine for one project but not for another!?
When you use the withInput() method, the data is flashed to the session as "old" data.
$request->old() should give you an array of all the "old" data.
$request->old('name') should give you the "old" name data.
I am using Laravel 5.
I wanted to redirect a user after a successful registration. I have tried these in my authcontroller but it doesn't work.
protected $loginPath = '/plan';
protected $redirectTo = '/plan';
protected $redirectAfterRegister = 'plan/';
These work on successful login though but not after registration.
I have also tried postRegister to render a view but using a postRegister method overrides the registration process and I don't want to do that. I just want to redirect the user to a page on successful registration.
There are two options to specify where to redirect the user in the
app/Http/Controllers/Auth/RegisterController.php
For a simple URL you can override this property.
protected $redirectTo = '/home';
If you have a more complicated logic than just one static URL, since Laravel 5.3 you can add a method in the same class RegisterController, with name redirectTo():
protected function redirectTo()
{
if (auth()->user()->role_id == 1) {
return '/admin';
}
return '/home';
}
The method behavior will override $redirectTo property value, even if the value is present.
Overriding the postRegister function like you mention should work, you would do this in your AuthController:
public function postRegister(Request $request)
{
$validator = $this->registrar->validator($request->all());
if ($validator->fails())
{
$this->throwValidationException(
$request, $validator
);
}
$this->auth->login($this->registrar->create($request->all()));
// Now you can redirect!
return redirect('/plan');
}
Or something like it. Copy it from the AuthenticatesAndRegistersUsers that is used in the top of your AuthController, here you will find all the functions
For this to work your AuthController should use the 'AuthenticatesAndRegistersUsers' trait, which is there by default.
More info about redirects in case you want to redirect to a named route: http://laravel.com/docs/5.0/responses#redirects
Simply add below line to AuthController class in Auth/AuthController.php
protected $redirectPath= '/plan';
Above redirect path will be used for successful login and successful register.
You can also modify the return of register() in RegisterUsers.php:
public function register(Request $request)
{
$validator = $this->validator($request->all());
if ($validator->fails()) {
$this->throwValidationException(
$request, $validator
);
}
Auth::guard($this->getGuard())->login($this->create($request->all()));
// Originally the parameter is $this->redirectPath()
return redirect()->to('/plans');
}
Here is laravel 5.4 solution
/**
* The user has been registered.
*
* #param \Illuminate\Http\Request $request
* #param mixed $user
* #return mixed
*/
protected function registered(Request $request, $user)
{
//User register now here you can run any code you want
if (\Request::ajax()){
return response()->json([
'auth' => auth()->check(),
'intended' => $this->redirectPath(),
]);
exit();
}
return redirect($this->redirectPath());
}
keep in mind register() Handle a registration request for the application.
where registered() called when user created.
after run command
php artisan make:auth
laravel create auth controller and resource files for change the route of register user
just go to below path
App\Http\Controllers\Auth\RegisterController
and change the protected $redirectTo param
and you can user this way in LoginController next of RegisterController
for redirect after login
I'm using laravel 5 to develop an app that allow every user to update his profile.
in order to update password, the user needs to first enter his old password and if the old password matched then his newly entered password will be hashed and stored in DB.
how can I validate this, using laravel form request validation?
I created a custom validator and added it to AppServiceProvider like this:
<?php
namespace App\Providers;
use Validator;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Hash ;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
Validator::extend('password_hash_check', function($attribute, $value, $parameters, $validator) {
return Hash::check($value , $parameters[0]) ;
});
}
then I used it in my form request validator like this:
<?php
namespace App\Http\Requests;
use App\Http\Requests\Request;
class UpdateUserProfileRequest extends Request
{
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
$hashed_password = $this->user()->password ;
return [
'oldPassword'=> "password_hash_check:$hashed_password|string|min:6",
'newPassword' => 'required_with:oldPassword|confirmed|min:6',
];
}
When you want to check a Hashed value generated by
Hash::make()
you need to use
Hash::check('unhashed', $hashed)
Every time you run Hash::make('string'), a different hash is made and will not match the previous one. For example:
// Generate a hash
$password = Hash::make('password');
// $password == $2y$08$T9r9qUxrr6ejs9Ne.nLzMet8l0A8BM5QvLjhaaJasgsbMBdX4JjRu
// Generate a new hash
$new_password = Hash::make('password');
// $new_password == $2y$08$3KBlYKIMpIvk.TWwim9oPuwGA.Pzv1iF7BsDyYkz7kQlhkA/ueULe
// Compare hashes the WRONG way
$password === $new_password; // false
// Compare hash the RIGHT way
Hash::check('password', $password); // true
Hash::check('password', $new_password); // true
So Use Hash::make() method of Hash class.
I'm not sure but I think that there is no native way to do this in Laravel. If so, you can implement a custom "hash" validator:
class CustomValidator extends \Illuminate\Validation\Validator {
public function validateHash($attribute, $value, $parameters)
{
$expected = $parameters[0];
return Hash::check($value, $expected);
}
}
Register it in a provider:
class AppServiceProvider extends ServiceProvider {
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
require_once __DIR__ . '/../Http/helpers.php';
Validator::resolver(function($translator, $data, $rules, $messages)
{
return new CustomValidator($translator, $data, $rules, $messages);
});
}
// ...
}
And use it in a form request:
class MyFormRequest extends FormRequest {
public function rules()
{
$password = Auth::user()->password;
return [
'old_password' => "required|hash:" . $password
]
}
// ...
}
Link to documentation:
http://laravel.com/docs/5.0/validation#custom-validation-rules