Getting user current status in laravel middleware - php

I am using this package for maintenance but to define what user can have access to the site during down time and what user can't I have issue.
during passed few days I was searching and I read a lot, that because main controller loads after middleware it can't detect user status whether is login or not. (I have no idea on that I just saw that repeatedly)
Anyway here is the issue:
I want allow users with role of Admin access to the site in down time, And visitors, other group of users don't.
What I did so far
based on package documentation I've made custome file in App\Exemptions\AdminExemption.php with this data:
<?php
namespace App\Exemptions;
use Auth; use Config; use App\User; use MisterPhilip\MaintenanceMode\Exemptions\MaintenanceModeExemption;
class AdminExemption extends MaintenanceModeExemption {
public function isExempt()
{
if (Auth::check() && Auth::user()->role == 'Admin') {
return true;
}
else {
return false;
}
//if user is logged and it's admin show the site
//if user is logged and isn't admin hide the site
//if user isn't logged (is visitor) hide the site
} }
I register this file in package config file config\maintenancemode.php
'exemptions' => [
App\Exemptions\AdminExemption::class,
],
and replaced package class with laravel default in Kernel
protected $middleware = [
// \App\Http\Middleware\CheckForMaintenanceMode::class,
\MisterPhilip\MaintenanceMode\Http\Middleware\CheckForMaintenanceMode::class,
//others...
]
Issue
Auth::check() or auth()->user() or Auth::user() none of this can detect logged user and assume the user is not login (is visitor). So the website is shut for everyone even admins.
Question
How can I get the real status of my current user in AdminExemption.php file?
Status
Is user (is admin) show the site
Is user (isn't admin) don't show the site
Is not user (visitor) don't show the site
Any idea?

You've registered the middleware in the main middleware stack, which will occur before it gets to the middleware groups. You'll likely want to move the middleware lower down in the Kernel into the web middleware stack. This is because Laravel will only be aware of a logged in user if there is a session - and the session is only started in the web stack.
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
You'll need to place the middleware after the StartSession middleware - but you're probably best just to pop it on to the end there, and then the logged in status should be available in your middleware.

Related

Laravel redirect on unauthorized action

I'm using Laravel 7 and appzcoder/laravel-admin package. my system is already pretty extensive and I have been able to work with authentication, authorization, roles and permissions fine so far. Today when my users try to do an action and they don't have authorization for it laravel gates and polices throw an unathorized exception and redirect them to a 403 not authorized page.
What I wish was that everytime my users don't have permission, laravel redirected them back with a message. How can I do that?
Remembering that My Guards and Polices are auto-generated with my permissions table rows as per appzcoder/laravel-admin functionality.
This may be the problem of CSRF token CSRF is enabled by default on all Routes in Laravel > 5, you can disable it for specific routes by modifying app/Http/Middleware/VerifyCsrfToken.php
class VerifyCsrfToken extends BaseVerifier
{
protected $except = [
// Place your URIs here
];
}
place all your unauthorized routes in this file seperated by comma.
example :
protected $except = [
'users/get_some_info',
];

Laravel 5.6 - How to authenticate API using sessions for same folder SPA?

I have a React SPA in the same Laravel project. The login/signup/logout and all other js views are in the js folder and use axios api calls for all POST/GET requests. I want to use the default Laravel session based web authentication for the embedded SPA, since it's in the same project folder and it will be the only javascript client accessing it. This api does not need to be open to the public, just for this react app, and it's an SPA for the speed and good user experience instead of full page reloads.
I've tried using Passport before, and for over a month, I still can't get it to work as intended. I do not want to deal with tokens, access tokens, refresh tokens, revoking tokens, CSRF, etc. Just the out of the box simple Laravel session based auth that works so easily on web, but want it to work on my react app. The only blade file is the index.blade.php which includes the react app.js
Any idea how we can accomplish this?
UPDATE 1:
After implementing #ceejayoz's suggestion:
You have to add the various Session/Cookie middlewares in
app/Http/Kernel.php (stuff like
\Illuminate\Session\Middleware\StartSession::class) to the API routes.
I added to $middlewareGroups.api to match the web middleware in app/Http/Kernel.php:
'api' => [
'throttle:60,1',
'bindings',
// Newly added middleware to match web middleware
\App\Http\Middleware\EncryptCookies::class
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
I realized there are two issues that occurred:
In the sessions table, even if not logged in, when loading app home page (or any page), multiple sessions are inserted into the sessions table. Shouldn't a new single session be inserted into this table only after user login?
After user log in, when refreshing the page manually in the browser and a call is made to a protected route, I get a 401 Unauthenticated which points me to this method in Illuminate/Auth/GuardHelpers.php:
public function authenticate() {
if (! is_null($user = $this->user())) {
return $user;
}
throw new AuthenticationException; // throws this 401 exception on logged in page refresh when fetching data from private route
}
Some additional notes:
In config/auth.php I updated the guards.api.driver to session instead of token.
In routes/api.php I have the protected routes wrapped in auth middleware like this: Route::group(['middleware' => 'auth'], function() { PRIVATE ROUTES HERE }
In config/session.php I have 'domain' => '.mydomain.com'
I am sending back these headers with each axios api request like this:
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
let token = document.head.querySelector('meta[name="csrf-token"]');
window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
Any idea how we can fix these 2 issues?
Looks like your session was not persistent.
Check if you changed any values in config/session.php that might create problems.
You can check default sesion config values here
From the comments, #Wonka solved his problem by changing
'same_site' => 'strict'
to
'same_site' => null
It's doable (and I've done the same myself for some apps).
By default, the routes in routes/api.php don't have sessions available, but you can add the various Session/Cookie middlewares in app/Http/Kernel.php (stuff like \Illuminate\Session\Middleware\StartSession::class) to the API routes.
You can, as #ljubadr suggested, also put the API routes right in routes/web.php instead, although that'd probably mean you'd need to make other changes (like removing CSRF protection from the web routes).

Unable to access sessions in Laravel app\Exceptions\Handler.php

I'm trying to display 404 page based upon user login, for which I'm trying to get the logged in user id from sessions.
But session session()->all() always returns empty array and also auth()->id(); & Auth::id(); are also empty value.
That's because in app/Http/Kernel.php the protected $middleware array doesn't have the middleware for the session.
What you can do is to move the \Illuminate\Session\Middleware\StartSession::class, middleware from the web middlware group to the global $middleware array.

Laravel : Multiple type users

I am working on an application, Where i have three type of users(real scenario), The application have three areas Freelancers and Lms (Learning Management systems) and admin panel for both :
Admins => Users of the admin panel, Where all the statistics/data is there.
Freelancers section : Freelancers section signin/signup
Lms section => Learning management system section's users signin/signup
Currently i am not using any kind of multi auth functionalities, And whenever i login in a user in the freelancer section and i goes to the lms section the authenticated user is available there.
As i am using only one table for the users where i have a column userType(Which is not in use but there for future proofing).
I know a couple of packages Like this one. which i can implement but i haven't and thought that there might be a better way and stackoverflow community can provide one.
My question is how to handle this type of situation, Whats the most efficient and robust way.
This is how I would do it.
I am going to skip long details about setting up auth controllers and views. They are quite straight forward to scaffold using the artisan console.
The first thing we need is new field on your Users table. An admin field if you only have two levels (admin and non-admin). An ENUM value in your case.
Depending on what the value in that field is you want to grant (or not) access to certain sections/pages/resources etc.
The artisan console generates all the necessary pages, middle-ware, routes for a basic login. But after you have done that you will need a second middle-ware to check for different levels of access. Lets call it CheckAdmin.
Use the following command
php artisan make:middleware CheckAdmin. This creates a new middleware with specified name in app\Http\Middleware
Now register the middleware in Kernel.php (Last line of code). This gives the middleware class that we just created a name (admin in this case).
protected $routeMiddleware = [
'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'admin' => \App\Http\Middleware\CheckAdmin::class,
];
All the routes in your that check for certain admin rights should use the admin middleware we just registered right after the auth factory middleware provided with laravel. There are two ways of doing this depending how you are building your app/website.
A. NOT using a resources controllers for the route.
Go to your routes web.php file. And register the auth and admin middleware for a request. An example follows.
Route::get('/example-admin-only-route', function () { //SOME LOGIC
})->middleware('auth', 'admin');
B. USING resources controllers. (Which you probably should whenever you can)
In the constructor of the resource controller. ExampleController.php for our Example resource.
class ExampleController extends Controller
{
public function __construct()
{
$this->middleware('auth');
$this->middleware('admin');
}
...
}
Write the logic for your CheckAdmin middleware.
namespace App\Http\Middleware;
use Closure;
class CheckAdmin
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if($request->user()->admin == 'Admin') //or FreeLancers or LMS (or maybe Unicorns)
{
return $next($request);
}
else
{
return redirect("/not-worthy");
}
}
}
The code checks the admin privileges and either lets the request pass or does something else with it. In our-case redirects to a different end-point. But you may wish to do more creative things with it.
Cheers.
It's probably not multiauth you're looking for but permissions on certain pages. Take a look at: https://github.com/spatie/laravel-permission
It's very simple and straightforward. It even lets you single out users, that for example both have admin level, but just one can view it.
The most efficient and robust way is to use simple user_type column to store user type and create some helpers. For example, to check if user is an admin you can create something like this in the User model:
public function isAdmin()
{
return auth()->check() && auth()->user()->user_type === 3;
}
This is the simplest method with a lot of advantages. It's good for apps that use a lot of relationships, it's good for speed etc.

Access Session Variable in Laravel Custom Error Pages

I am trying to create a custom error page in Laravel 5.3. I wanted the retrieve some variable stored in the Session like Session::get("username"), however what I get is an empty variable.
How am I suppose to get any Session variable in a custom error page in Laravel or is it not possible?
As Laravel did not find the route, you did not pass through the middleware \Illuminate\Session\Middleware\StartSession (which is by default in web middleware group), so your session is not started.
To have your session initialized, move \Illuminate\Session\Middleware\StartSession::class, from web to $middleware in Kernel.php:
protected $middleware = [
\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
\Illuminate\Session\Middleware\StartSession::class,
],
Middlewares in $midleware are always run so your session will be initialized, even if the route is not found.

Categories