I have created the routes and views needed for authentication using one simple command:
php artisan make:auth
Everything works fine login and register sections. However when I go to my controller's constructor to check if the user its logged in I always get false response; even though the user its logged in!
public function __construct()
{
dd(Auth::check());
}
Any idea?! And yes I did use Auth; at the top.
Middleware (and therefore setting the logged in user) don't happen until after the controller constructor. See this related question/answer for more details:
Laravel 5 Auth is non object in Controller construct
Use the helper function auth()->check() and add
$this->middleware('auth') to the function __construct() method.
Related
I am building a Laravel 7.x. application that will combine both standard authentication (user logs on via form) and API authentication through Sanctum (token based). I want to generate a sanctum API token during successful user authentication. To do that, I need to hook into the login flow.
Standard authentication was scaffolded to my application by running php artisan ui vue --auth.
When I inspect routes/web.php, I can see only Auth::routes(); which under the hood allegedly generates the classic routes I was used to in previous Laravel versions. Taken from the answer I linked, /login route definition that is generated looks like this:
$this->post('login', 'Auth\LoginController#login');
However, when I inspect my LoginController that was scaffolded, I can not see any of the methods that should be generated by Auth::routes(). There is nothing in there and it looks like everything is handled transparently to me as a developer:
class LoginController extends Controller
{
use AuthenticatesUsers;
protected $redirectTo = RouteServiceProvider::HOME;
public function __construct()
{
$this->middleware('guest')->except('logout');
}
}
How do I hook into the login flow and add my own actions to it?
I can see, two questions here and I will try to answer them as good as I can.
1. Execute action on successful user authentication
I think the cleanest way to achieve this, it to utilize the Laravel event / listeners architecture.
In app/Providers/EventServiceProvider.php extend the $listen array
// include at the top
use Illuminate\Auth\Events\Login;
protected $listen = [
// other handlers [],
Login::class => [
CreateUserApiToken::class,
],
];
Then execute this artisan command, which will magically create your listener file
php artisan event:generate
Now you can open app/Providers/CreateUserApiToken.php and put whatever you like in the handle function.
public function handle(Login $event)
{
dd($event->user);
}
2. Where's the actual laravel code?
For lots of Laravel classes, you will only find a minimal amount of code projected directly to your app directory. Most of it is hidden in traits or by extending parent classes. Let's take the LoginController for example
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use AuthenticatesUsers;
This is the trait that controller is using. And especially the top line gives you a pretty good clue where that file is located. You could also use your code editors search function, to search in all files for trait AuthenticatesUsers.
In this case the corresponding file would be vendor/laravel/ui/auth-backend/AuthenticatesUsers.php. Here you will find most of the functions that you are looking for. Of course, it's not a good idea to overwrite that file directly, because all the changes would be lost, if the laravel framework get's updated.
But if you find a function in there, that you want to change, let's say showLoginForm() for example, you can simply include that function in your LoginController and change the code.
public function showLoginForm()
{
return view('example.login');
}
I am building an application with multiple user roles and actions. I did follow the official laravel doc (https://laravel.com/docs/5.5/middleware#middleware-parameters).
But in my controller's constructor (from where I call the above middleware) I am using Auth facade to get user details. I know how to use Auth facade, I had implemented it on several places inside my application. But when I use it inside the constructor it returns null (in logged in condition - I double checked that).
I implemented it like this, I have to call two controllers(since only registered users can access that page)
public function __construct()
{
$role = Auth::user()->role;
$this->middleware('auth');
$this->middleware('checkRole:$role');
}
PS: I tried to initialize $role variable as protected and outside the constructor , still not working. Any suggestions will be helpful
Thank you.
That's because constructors are created before middlewares,that's why its returning null.
This answer will propably solve your problems: Can't call Auth::user() on controller's constructor
If you are using the same user table for both "front-end" user and "admin" & want to apply condition in admin controller's constructor.
You can use below.
auth()->user()
And in the constructor you can use below code.
public function __construct(){
$this->middleware(function ($request, $next) {
if(auth()->user()->hasRole('frontuser')){
return redirect()->route('home')->withFlashMessage('You are not authorized to access that page.')->withFlashType('warning');
}
return $next($request);
});
}
But I prefer to handle these in separate middleware class instead of writing this in the controllers constructor.
I have setup my model policy and it seems to be working when I authorize actions from within controller actions.
// create action
public function create()
{
$this->authorize('create', BusinessProfile::class);
return view('business-profile.create');
}
The policy for create simply returns true or false and switching the Boolean seems to be working as I am authorized based on it.
This conforms that my policies are set up correctly.
However, instead of using the authorize method everywhere in my controller, I tried to set up the middleware in my constructor.
The Laravel documentation shows this example.
Route::post('/post', function () {
// The current user may create posts...
})->middleware('can:create,App\Post');
So, I wrote this in my controller constructor.
public function __construct()
{
$this->middleware('auth');
$this->middleware('can:create,BusinessProfile')->only('create');
}
However, when doing this, the action is always unauthorized.
Bonus Information
I went ahead and wrote garbage code in my policy to raise a syntax error and still, I get an unauthorized response which tells me my policy is not firing at all. It could be that I have not registered my policy correctly but as mentioned above, $this->authorize(...) works as expected.
It seems there you used alias for your model while it requires model name. At documentation states:
some actions like create may not require a model instance. In these
situations, you may pass a class name to the middleware. The class
name will be used to determine which policy to use when authorizing
the action:
You can find more information here: https://laravel.com/docs/5.4/authorization#policy-methods
So in the controller constructor this line:
$this->middleware('can:create,BusinessProfile')->only('create');
will become:
$this->middleware('can:create,App\BusinessProfile')->only('create');
I am trying to get the authenticated user in the constuctor of my controller in laravel by doing dd(auth()->user()); and it says null. I even added the user id into a request attribute in one of my middleware like so:
$request->attributes->add(['auth_user_id' => $user_id]);
Even if I do dd($request->get('auth_user_id') in my controller's construct method, I get null. But when I do the same thing in a test route, Both die dump statements work well and give me back the user or the user id, whichever I ask for.
Why am I not able to get these in the construct method of my controller tho? I am even able to get the same user id and auth user in my controller method to which the request goes to. Just not the construct method. What am I missing?
With Laravel 5.3, this change was introduced where middleware are initialized after the controller class is constructed. This means app-critical middleware like Auth --specifically Auth::user() are not available to the controller's __construct() method.
Please refer this documentation.
https://github.com/laravel/docs/blob/5.3/upgrade.md#session-in-the-constructor
Edit
This way you can implement what you needed.
This will allow registering a closure middleware in the controller's constructor, so that any auth/session stuff can be called and set as properties on the controller:
public function __construct()
{
$this->middleware(function ($request, $next) {
$this->user = $request->user();
return $next($request);
});
}
Refer this link by Controller closure middleware - JosephSilber
I think this is because constructor method called when the object of class initialized and at that time you are not logged in and when you are not logged in you cannot get the auth_user_id.
But In case of normal method, they are called after constructor method, and you are logged in that's why you are able to get the auth_user_id
I need to be able to do a post method to the default "/register" from a form even when im already authenticated.
Currently its not possible, I get no errors but I don't get a record in my database.
My form and code works when im not logged in.
Where do i need to make changes so it will work?
I think its near the registersusers.php but that code is completely new to me.
By default you cannot do this because the guest middleware stops logged in users from accessing the registration page.
In your AuthController (provided by Laravel in app/Http/Controllers/Auth) you can specify which methods should remain accessible to non-guests (or authenticated users).
By default only the logout method is available to logged in users, however you can add anymore you wish.
public function __construct()
{
$this->middleware($this->guestMiddleware(), ['except' => ['logout', 'showRegistrationForm', 'register']]);
}
showRegistrationForm is responsible for showing the login page and form, register is responsible for processing the registration and persisting it.
Technically, it's working as intended. A logged in user shouldn't be able to register a user, that doesn't make sense.
However, you can simply use the RegistersUsers trait within your own Controller in order to attempt to register a user. It's as simple as this:
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class RegistrationController extends Controller {
uses RegistersUsers;
public function create(Request $request){
return $this->postRegister($request);
}
}
The ->postRegister() method is provided by the RegistersUsers trait. It accepts an \Illuminate\Http\Request object and is injected as a dependency, so you must only pass an instance of \Illuminate\Http\Request. You can easily display the registration form as well by simply just calling $this->showRegistrationForm();, such as this:
public function show(){
return $this->getRegister();
}
Now just attach your routes accordingly.