Call to a member function getEmailForPasswordReset() on null - laravel

I do not know why I get this error when I try to reset password.
I have another project with the same code and it works.
I noticed the error is in the "resources/views/auth/emails/password.php" file, that does have a null $user variable.
The strange thing is that in the function the view is called, the $user variable has value.
When the variable is passed to the view, it losts value.
public function emailResetLink(CanResetPasswordContract $user, $token, Closure $callback = null)
{
// We will use the reminder view that was given to the broker to display the
// password reminder e-mail. We'll pass a "token" variable into the views
// so that it may be displayed for an user to click for password reset.
$view = $this->emailView;
**// HERE THE $user VARIABLE HAS VALUE.**
**// IN THE $view VIEW THE $user VARIABLE IS NULL**
return $this->mailer->send($view, compact('token', 'user'), function ($m) use ($user, $token, $callback) {
$m->to($user->getEmailForPasswordReset());
if (! is_null($callback)) {
call_user_func($callback, $m, $user, $token);
}
});
}
UPDATED
This is the user model:
<?php
namespace App;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Zizaco\Entrust\Traits\EntrustUserTrait;
class User extends Authenticatable
{
use EntrustUserTrait; // add this trait to your user model
protected $fillable = [
'name', 'email'
];
protected $hidden = [
'password',
];
}

Got it,
the problem was a ViewComposer I had created and forgot.
This is the composer service provider:
class ComposerServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*
* #return void
*/
public function boot()
{
// Using class based composers...
View::composer(
'*', 'App\Http\ViewComposers\UserComposer'
);
}
/**
* Register the application services.
*
* #return void
*/
public function register()
{
//
}
}
And this is (edited to make it work) the UserComposer class:
class UserComposer
{
/**
* constructor.
*/
public function __construct()
{
}
/**
* Bind data to the view.
*
* #param View $view
* #return void
*/
public function compose(View $view)
{
$excludedViews = ['auth.emails.password'];
if (!in_array($view->getName() , $excludedViews))
{
$view->with('user', session('user'));
}
}
}
With the "excludedViews" variable I can inject "user" variable in all routes except the reset password.

Related

Laravel 8 Gate issue iam trying to check condition with different model but there are error show

In my laravel 8 iam define gate but there some problem my gate is accept only one model name is that Admin when i try to check another model name there are error show
here is my authserviceprovider
<?php
namespace App\Providers;
use App\Models\Admin\Role;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Gate;
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* #var array
*/
protected $policies = [
// 'App\Models\Model' => 'App\Policies\ModelPolicy',
];
/**
* Register any authentication / authorization services.
*
* #return void
*/
public function boot()
{
$this->registerPolicies();
Gate::define('isAdmin', function(Role $role) {
if ($role->role === 'Admin') {
return true;
} else {
return false;
}
});
}
}
here is controller
public function index(Role $role)
{
if (!Gate::allows('isAdmin', $role))
{
abort(403);
}
$users = Admin::with('roles')->get();
return view('Admin.user.index', compact('users'));
}
error message
TypeError
App\Providers\AuthServiceProvider::App\Providers{closure}(): Argument #1 ($role) must be of type App\Models\Admin\Role, App\Models\Admin given, called in D:\xampp\htdocs\education\vendor\laravel\framework\src\Illuminate\Auth\Access\Gate.php on line 477
http://127.0.0.1:8000/admin/users
Gate are mostly used to authored login user. if you need to authorise in any model specific then use policy
so in gate we get login user instance as call back automatically
so in your case code will be like this
/**
* Register any authentication / authorization services.
*
* #return void
*/
public function boot()
{
$this->registerPolicies();
Gate::define('isAdmin', function($user) {
return $user->role->name === 'Admin';
});
}
then in controller
public function index(Role $role)
{
abort_if(!Gate::allows('isAdmin'));
$users = Admin::with('roles')->get();
return view('Admin.user.index', compact('users'));
}

Laravel 8 Multi Auth with Jetstream livewire

im trying to setup a multi auth system in laravel 8 with jetstream livewire in my ecomm project (one login page for admins(/admin/login) and another for users(/login))
i have followed a tutorial and everything is ok expect when i login to user from /login page i can access /admin/dashboard with that user and with admin its fine and cant access user /dashboard
routes\web.php:
Route::get('/', function () {
return view('welcome');
});
Route::group(['prefix'=>'admin','middleware'=>['admin:admin']],function(){
Route::get('/login', [AdminController::class, 'loginForm']);
Route::post('/login', [AdminController::class, 'store'])->name('admin.login');
Route::get('/logout', [AdminController::class, 'Logout'])->name('admin.logout');
});
Route::middleware(['auth:sanctum,admin', 'verified'])->get('/admin/dashboard', function () {
return view('admin.index');
})->name('dashboard.admin');
Route::middleware(['auth:sanctum,web', 'verified'])->get('/dashboard', function () {
return view('dashboard');
})->name('dashboard');
Models\Admin.php:
<?php
namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Fortify\TwoFactorAuthenticatable;
use Laravel\Jetstream\HasProfilePhoto;
use Laravel\Sanctum\HasApiTokens;
class Admin extends Authenticatable
{
use HasApiTokens;
use HasFactory;
use HasProfilePhoto;
use Notifiable;
use TwoFactorAuthenticatable;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password',
'remember_token',
'two_factor_recovery_codes',
'two_factor_secret',
];
/**
* The attributes that should be cast to native types.
*
* #var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
/**
* The accessors to append to the model's array form.
*
* #var array
*/
protected $appends = [
'profile_photo_url',
];
}
Controllers\AdminController.php:
<?php
namespace App\Http\Controllers;
use Illuminate\Contracts\Auth\StatefulGuard;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Illuminate\Routing\Pipeline;
use App\Actions\Fortify\AttemptToAuthenticate;
use Laravel\Fortify\Actions\EnsureLoginIsNotThrottled;
use Laravel\Fortify\Actions\PrepareAuthenticatedSession;
use App\Actions\Fortify\RedirectIfTwoFactorAuthenticatable;
use App\Http\Responses\LoginResponse;
use Laravel\Fortify\Contracts\LoginViewResponse;
use Laravel\Fortify\Contracts\LogoutResponse;
use Laravel\Fortify\Features;
use Laravel\Fortify\Fortify;
use Laravel\Fortify\Http\Requests\LoginRequest;
use Auth;
class AdminController extends Controller
{
/**
* The guard implementation.
*
* #var \Illuminate\Contracts\Auth\StatefulGuard
*/
protected $guard;
/**
* Create a new controller instance.
*
* #param \Illuminate\Contracts\Auth\StatefulGuard
* #return void
*/
public function __construct(StatefulGuard $guard, Request $request)
{
$this->guard = $guard;
}
public function loginForm(){
return view('admin.login',['guard'=>'admin']);
}
public function Logout(){
Auth::logout();
return Redirect()->url('admin/login')->with('success', 'Logged Out');
}
/**
* Show the login view.
*
* #param \Illuminate\Http\Request $request
* #return \Laravel\Fortify\Contracts\LoginViewResponse
*/
public function create(Request $request): LoginViewResponse
{
return app(LoginViewResponse::class);
}
/**
* Attempt to authenticate a new session.
*
* #param \Laravel\Fortify\Http\Requests\LoginRequest $request
* #return mixed
*/
public function store(LoginRequest $request)
{
return $this->loginPipeline($request)->then(function ($request) {
return app(LoginResponse::class);
});
}
/**
* Get the authentication pipeline instance.
*
* #param \Laravel\Fortify\Http\Requests\LoginRequest $request
* #return \Illuminate\Pipeline\Pipeline
*/
protected function loginPipeline(LoginRequest $request)
{
if (Fortify::$authenticateThroughCallback) {
return (new Pipeline(app()))->send($request)->through(array_filter(
call_user_func(Fortify::$authenticateThroughCallback, $request)
));
}
if (is_array(config('fortify.pipelines.login'))) {
return (new Pipeline(app()))->send($request)->through(array_filter(
config('fortify.pipelines.login')
));
}
return (new Pipeline(app()))->send($request)->through(array_filter([
config('fortify.limiters.login') ? null : EnsureLoginIsNotThrottled::class,
Features::enabled(Features::twoFactorAuthentication()) ? RedirectIfTwoFactorAuthenticatable::class : null,
AttemptToAuthenticate::class,
PrepareAuthenticatedSession::class,
]));
}
/**
* Destroy an authenticated session.
*
* #param \Illuminate\Http\Request $request
* #return \Laravel\Fortify\Contracts\LogoutResponse
*/
public function destroy(Request $request): LogoutResponse
{
$this->guard->logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return app(LogoutResponse::class);
}
}
Responses\LoginResponse.php:
<?php
namespace App\Http\Responses;
use Laravel\Fortify\Contracts\LoginResponse as LoginResponseContract;
class LoginResponse implements LoginResponseContract
{
/**
* Create an HTTP response that represents the object.
*
* #param \Illuminate\Http\Request $request
* #return \Symfony\Component\HttpFoundation\Response
*/
public function toResponse($request)
{
return $request->wantsJson()
? response()->json(['two_factor' => false])
: redirect()->intended('admin/dashboard');
}
}
i also created a copy of StatefulGuard in App\Guards\AdminStatefulGuard.php following that tutorial but never used it.
Problem fixed by adding this code to my admin controllers.
public function __construct()
{
$this->middleware(['auth:admin,admin', 'verified']);
}
and also replacing this in web route:
Route::middleware(['auth:sanctum,admin', 'verified'])->get('/admin/dashboard', function () {
return view('admin.index');
})->name('dashboard.admin');
with this:
Route::middleware(['auth:admin,admin', 'verified'])->get('/admin/dashboard', function () {
return view('admin.index');
})->name('dashboard.admin');
Try this on Admin middleware
public function handle(Request $request, Closure $next, $guard)
{
if (Auth::guard($guard)->check()) {
return redirect('/admin/dashboard');
}
if(Auth::guard('web')->check()) {
return redirect('/dashboard');
}
return $next($request);
}
and on RedirectIfAuthenticated middleware:
public function handle(Request $request, Closure $next, ...$guards)
{
$guards = empty($guards) ? [null] : $guards;
foreach ($guards as $guard) {
if (Auth::guard($guard)->check()) {
return redirect(RouteServiceProvider::HOME);
}
}
if(Auth::guard('admin')->check()) {
return redirect('admin/dashboard');
}
return $next($request);
}

Session of logged user return undefined

I have custom login function in Laravel 5.4 which seems to work but not exactly. What I have in UserController.php is
public function loginSubmit()
{
$user = User::where('username', Input::get('username'))->first();
if (!$user) {
$validator->messages()->add('username', 'Invalid login or password.');
return Redirect::to('/users/login')->withErrors($validator->errors())->withInput(Input::except(['captcha']));
}
if (!Hash::check(Input::get('password'), $user->password)) {
$validator->messages()->add('username', 'Invalid login or password.');
return Redirect::to('/users/login')->withErrors($validator->errors())->withInput(Input::except(['captcha']));
}
$user->last_login = \Carbon\Carbon::now();
$user->save();
Session::put('user', ['user_id' => $user->user_id]);
//dd(Session::get('user', null));
return Redirect::to('/');
}
dd(Session::get('user', null)); return
array:1 [▼
"user_id" => 1
]
Which means that user with ID=1 is logged and is in stored in session. In BaseController.php which sharing the user session I have this
public static function isLoggedIn()
{
$user = Session::get('user', null);
if ($user !== null) {
return true;
} else {
return false;
}
}
But when I tried to show the username of the logged in user
{{ $user->username }}
I've got error
Undefined variable: user
This my Users model
namespace App;
use Illuminate\Database\Eloquent\Model;
use Eloquent;
use DB;
use Illuminate\Auth\Authenticatable;
use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
class User extends Eloquent implements AuthenticatableContract, CanResetPasswordContract
{
use Authenticatable, CanResetPassword;
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'users';
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
protected $hidden = array('password');
protected $primaryKey = 'user_id';
}
Any idea what is wrong with my session here?
It means that $user is not defined in the blade file you are using.
Call the view with the user as a parameter (i think this is the best approach):
$user = Session::get('user', null);
return view('yourview')->with(['user' => $user];
Or use the Session in your blade file:
{{ Session::get('user', null)->username }}
EDIT: to use View::share. You can put this inside the boot function of your Service provider:
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
$user = Session::get('user', null);
View::share('user', $user);
}
//...
}

Laravel authentication without global scope

In my Laravel app users can disable (not delete) their account to disappear from the website. However, if they try to login again their account should be activated automatically and they should log in successfully.
This is done with "active" column in the users table and a global scope in User model:
protected static function boot() {
parent::boot();
static::addGlobalScope('active', function(Builder $builder) {
$builder->where('active', 1);
});
}
The problem now is that those inactive accounts can't log in again, since AuthController does not find them (out of scope).
What I need to achieve:
Make AuthController ignore global scope "active".
If username and password are correct then change the "active" column value to "1".
The idea I have now is to locate the user using withoutGlobalScope, validate the password manually, change column "active" to 1, and then proceed the regular login.
In my AuthController in postLogin method:
$user = User::withoutGlobalScope('active')
->where('username', $request->username)
->first();
if($user != null) {
if (Hash::check($request->username, $user->password))
{
// Set active column to 1
}
}
return $this->login($request);
So the question is how to make AuthController ignore global scope without altering Laravel main code, so it will remain with update?
Thanks.
Create a class GlobalUserProvider that extends EloquentUserProvider like below
class GlobalUserProvider extends EloquentUserProvider {
public function createModel() {
$model = parent::createModel();
return $model->withoutGlobalScope('active');
}
}
Register your new user provider in AuthServiceProvider:
Auth::provider('globalUserProvider', function ($app, array $config) {
return new GlobalUserProvider($this->app->make('hash'), $config['model']);
});
Finally you should change your user provider driver to globalUserProvider in auth.php config file.
'providers' => [
'users' => [
'driver' => 'globalUserProvider',
'model' => App\Models\User::class
]
]
protected static function boot()
{
parent::boot();
if (\Auth::check()) {
static::addGlobalScope('active', function(Builder $builder) {
$builder->where('active', 1);
});
}
}
Please try this for login issue, You can activate after login using withoutGlobalScopes().
#Sasan's answer is working great in Laravel 5.3, but not working in 5.4 - createModel() is expecting a Model but gets a Builder object, so when EloquentUserProvider calls $model->getAuthIdentifierName() an exception is thrown:
BadMethodCallException: Call to undefined method Illuminate\Database\Query\Builder::getAuthIdentifierName() in /var/www/vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php:2445
Instead, follow the same approach but override more functions so that the right object is returned from createModel().
getQuery() returns the builder without the global scope, which is used by the other two functions.
class GlobalUserProvider extends EloquentUserProvider
{
/**
* Get query builder for the model
*
* #return \Illuminate\Database\Eloquent\Builder
*/
private function getQuery()
{
$model = $this->createModel();
return $model->withoutGlobalScope('active');
}
/**
* Retrieve a user by their unique identifier.
*
* #param mixed $identifier
* #return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function retrieveById($identifier)
{
$model = $this->createModel();
return $this->getQuery()
->where($model->getAuthIdentifierName(), $identifier)
->first();
}
/**
* Retrieve a user by their unique identifier and "remember me" token.
*
* #param mixed $identifier
* #param string $token
* #return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function retrieveByToken($identifier, $token)
{
$model = $this->createModel();
return $this->getQuery()
->where($model->getAuthIdentifierName(), $identifier)
->where($model->getRememberTokenName(), $token)
->first();
}
}
Sasan Farrokh has a right answer. The only thing not to rewrite createModel but newModelQuery and this will work
protected function newModelQuery($model = null)
{
$modelQuery = parent::newModelQuery();
return $modelQuery->withoutGlobalScope('active');
}
Extend the AuthController with the code you used in your OP. That should work.
public function postLogin(Request $request)
{
$user = User::withoutGlobalScope('active')
->where('username', $request->username)
->first();
if($user != null){
if (Hash::check($request->password, $user->password)){
$user->active = 1;
$user->save();
}
}
return $this->login($request);
}
I resolved it by creating the new package.
mpyw/scoped-auth: Apply specific scope for user authentication.
Run composer require mpyw/scoped-auth and modify your User model like this:
<?php
namespace App;
use Illuminate\Auth\Authenticatable;
use Illuminate\Contracts\Auth\Authenticatable as UserContract;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Mpyw\ScopedAuth\AuthScopable;
class User extends Model implements UserContract, AuthScopable
{
use Authenticatable;
public function scopeForAuthentication(Builder $query): Builder
{
return $query->withoutGlobalScope('active');
}
}
You can also easily pick Illuminate\Auth\Events\Login to activate User on your Listener.
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Event;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
class EventServiceProvider extends ServiceProvider
{
/**
* The event listener mappings for the application.
*
* #var array
*/
protected $listen = [
\Illuminate\Auth\Events\Login::class => [
\App\Listeners\ActivateUser::class,
],
];
/**
* Register any events for your application.
*
* #return void
*/
public function boot()
{
parent::boot();
//
}
}
 
<?php
namespace App\Listeners;
use Illuminate\Auth\Events\Login;
class ActivateUser
{
/**
* Handle the event.
*
* #param Illuminate\Auth\Events\Login $event
* #return void
*/
public function handle(Login $event)
{
$event->user->fill('active', 1)->save();
}
}
 
I had to use
->withoutGlobalScopes() instead
in order for it to work

Laravel Trying to get property of non-object

I am struggling to understand how laravel works and I have a very difficult time with it
Model - User.php the User model
<?php
use Illuminate\Auth\UserInterface;
use Illuminate\Auth\Reminders\RemindableInterface;
class User extends Eloquent implements UserInterface, RemindableInterface {
protected $fillable = array('email' , 'username' , 'password', 'code');
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'users';
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
protected $hidden = array('password');
public function Characters()
{
return $this->hasMany('Character');
}
/**
* Get the unique identifier for the user.
*
* #return mixed
*/
public function getAuthIdentifier()
{
return $this->getKey();
}
/**
* Get the password for the user.
*
* #return string
*/
public function getAuthPassword()
{
return $this->password;
}
/**
* Get the e-mail address where password reminders are sent.
*
* #return string
*/
public function getReminderEmail()
{
return $this->email;
}
}
Model - Character.php the character model
<?php
class Character extends Eloquent {
protected $table = 'characters';
protected $fillable = array('lord_id','char_name', 'char_dynasty', 'picture');
public function user()
{
return $this->belongsTo('User');
}
public function Titles()
{
return $this->hasMany('Title');
}
}
?>
routes.php
Route::group(array('prefix' => 'user'), function()
{
Route::get("/{user}", array(
'as' => 'user-profile',
'uses' => 'ProfileController#user'));
});
ProfileController.php
<?php
class ProfileController extends BaseController{
public function user($user) {
$user = User::where('username', '=', Session::get('theuser') );
$char = DB::table('characters')
->join('users', function($join)
{
$join->on('users.id', '=', 'characters.user_id')
->where('characters.id', '=', 'characters.lord_id');
})
->get();
if($user->count()) {
$user = $user->first();
return View::make('layout.profile')
->with('user', $user)
->with('char', $char);
}
return App::abort(404);
}
}
In my code I will redirect to this route with the following:
return Redirect::route('user-profile', Session::get('theuser'));
In the view I just want to do:
Welcome back, {{ $user->username }}, your main character is {{ $char->char_name }}
My problem is that I will receive this error: Trying to get property of non-object in my view. I am sure it is referring to $char->char_name. What's going wrong? I have a very difficult time understanding Laravel. I don't know why. Thanks in advance!
You should be using the Auth class to get the session information for the logged in user.
$user = Auth::user();
$welcome_message = "Welcome back, $user->username, your main character is $user->Character->char_name";
You don't need to pass anything to that route either. Simply check if the user is logged in then retrieve the data. You have access to this data from anywhere in your application.
if (Auth::check())
{
//the user is logged in
$user = Auth::user();
To answer your question in the comments, reading the documentation would solve all of these problems, however:
public function user()
{
if (Auth::check())
{
$user = Auth::user();
return View::make('rtfm', compact('user'));
}
else
{
return "The documentation explains all of this very clearly.";
}
}

Resources