Sharing same route with unauthenticated and authenticated users in Laravel 5.3 - laravel

I have a route that where I'm using an auth middleware and it works great.
Route::group(['prefix' => 'v1','middleware' => ['auth:api']], function()
{
Route::resource('user', 'v1\MyController');
});
The problem is that I would also like this route to be accessible to non-authenticated users as well. With the above, I get a 401 Unauthorized error and I can't return any content for unauthenticated users. So how can I authenticate this route (so it passes down the user data) while also allowing the route to proceed even if the user is NOT authenticated?
(I tried doing a conditional Auth check on the router page but it seems the user has gone through authentication yet so it always remains false.)
EDIT: I should also note that I'm using an API route with Password Grant & access tokens.

remove this route from current route group (which applies auth middleware).
then
public function __construct()
{
if (array_key_exists('HTTP_AUTHORIZATION', $_SERVER)) {
$this->middleware('auth:api');
}
}
then
if (Auth::check()) {
// Auth users
} else{
//Guest users
}

I am experiencing the same case.
since the auth middleware only checks for authenticated user, we can use client credentials for the non-authenticated user.
the client credentials have a separated middleware located in Laravel\Passport\Http\Middleware\CheckClientCredentails.
I have created a custom middleware to combine both middleware to allow either one is pass.
here is my custom middleware
namespace Laravel\Passport\Http\Middleware;
use Closure;
use League\OAuth2\Server\ResourceServer;
use Illuminate\Auth\AuthenticationException;
use League\OAuth2\Server\Exception\OAuthServerException;
use Symfony\Bridge\PsrHttpMessage\Factory\DiactorosFactory;
class CheckClientCredentials
{
/**
* The Resource Server instance.
*
* #var ResourceServer
*/
private $server;
/**
* Create a new middleware instance.
*
* #param ResourceServer $server
* #return void
*/
public function __construct(ResourceServer $server)
{
$this->server = $server;
}
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*
* #throws \Illuminate\Auth\AuthenticationException
*/
public function handle($request, Closure $next, ...$scopes)
{
$psr = (new DiactorosFactory)->createRequest($request);
try{
$psr = $this->server->validateAuthenticatedRequest($psr);
} catch (OAuthServerException $e) {
throw new AuthenticationException;
}
foreach ($scopes as $scope) {
if (!in_array($scope,$psr->getAttribute('oauth_scopes'))) {
throw new AuthenticationException;
}
}
return $next($request);
}
}
Kernal.php
protected $routeMiddleware = [
'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
'auth.api' => \App\Http\Middleware\APIAuthenticate::class,
....
routes\api.php
Route::group([
'namespace' => 'API',
'middleware' => 'auth.api:api',
], function(){
....

From within an unauthenticated (not assigned the auth:api middleware) route's handler method, try:
Auth::guard("api")->user();
If it's populated, then your unguarded route can treat the access as authenticated. If not, its a random user accessing the route, and can be treated as such.

Dont put those urls whome you want to allow for both guest users and authenticated users in auth middleware. Because auth middleware allow for only authenticated users.
To check for authenticated and unauthenticated user you can use following code in view
#if (Auth::guest())
//For guest users
#else
//for authenticated users
#endif
Edited : In controller use
if (Auth::check()) {
// Auth users
} else{
//Guest users
}

#Yves 's answer is nearly correct. But a small change,
Instead of array_key_exists, we need to check whether key value is not null. Because It always has that key but null value. SO, instead of controller construct check for authorization header like this.
if ($_SERVER['HTTP_AUTHORIZATION']) {
$this->middleware('auth:sanctum');
}
Then you can check for authenticated user like this:
if (auth()->check()) {
// User is logged in and you can access using
// auth()->user()
} else {
// Unauthenticated
}

for sanctum use:
if (Auth::guard('sanctum')->check()) {
// user is logged in
/** #var User $user */ $user = Auth::guard('sanctum')->user();
} else {
// user is not logged in (no auth or invalid token)
}

You can use this example:
#if(Auth::user())
echo "authincatesd user";
#else
echo "unauthorised";
#endif

Related

Too many redirect on laravel middleware

I have created a custom middleware and i am checking if the password field is null and redirect user to change the password but it give me redirection error, any one can help? Let me add more details i want user to redirect to /change-password if the password field is empty
so here's the whole process.
user verify the email, redirect to /change-password route instead of dashboard if password field in the database is empty other wise we redirect them to dashboard. Users shouldn't access any route until they didn't update the password.
Remember i am using laravel breeze for auth
Middleware code:
<?php
namespace App\Http\Middleware;
use App\Providers\RouteServiceProvider;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class ChangePasswordMiddleware
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
* #return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
public function handle(Request $request, Closure $next)
{
$user = Auth::user();
if (empty($user->password)){
return redirect()->route('patient.password');
} else{
return redirect()->intended(RouteServiceProvider::HOME);
}
return $next($request);
}
}
My Routes:
Route::middleware(['auth', 'verified', 'changepassword'])->group(function (){
Route::get('/change-password', [PatientsController::class, 'passwordView'])->name('patient.password');
Route::get('/dashboard', [PatientsController::class, 'index'])->name('patient.dashboard');
Route::get('pricing', [PatientsController::class, 'pricing'])->name('patient.pricing');
});
changepassword is registered in my kernel.php and it's a custom middleware.
i have tried to create a different group for routes but it still doesn't work, i want changepassword middleware to force use to change the password and other routes shouldn't work until the password field is updated
As mentioned in the comments, the middleware is being called over and over because the password is empty. Hence, the issue of too many redirects. Your routes must ignore the route for /change-password.
Route::middleware(['auth', 'verified', 'changepassword'])->group(function (){
Route::get('/change-password', [PatientsController::class, 'passwordView'])
->name('patient.password')
->withoutMiddleware([\App\Http\Middleware\ChangePasswordMiddleware::class]);
...
...
});
After this your too many redirects problem should go away.
Also, make sure your if/else logic is correct in the handle() method. The else logic looks odd to me.
Try
public function handle(Request $request, Closure $next)
{
$user = Auth::user();
if (empty($user->password)){
abort(302, 'Please change your password', ['Location' => route('patient.password')]);
}
return $next($request);
}
If the password is empty, it automatically redirects to /change-password route.

Get Requested middleware list from $request?

In my project created with Laravel 8 with vue+ inertia + fortify package, I use two guards one for normal users and one for admins. but there only have one login view redirect.
I just want to show different login to normal users and another login to admins. it should detect by middleware used in the route. I can filter it, if I can get the requested guard name from there.
Here is my example route:
<?php
//'auth:users' is normal users guard
Route::group(['middleware' => 'auth:users'], function () {
Route::prefix('/account')->name('account.')->group(function () {
Route::get('/', [AccountController::class, 'index'])->name('index');
});});
//'auth:web' is admin users guard
Route::group(['middleware' => 'auth:web'], function () {
Route::prefix('/admin')->name('admin.')->group(function () {
Route::prefix('/account')->name('account.')->group(function () {
Route::get('/', [AdminAccountController::class, 'index'])->name('index');
});
});
});
auth middleware:
<?php
namespace App\Http\Middleware;
use Illuminate\Auth\Middleware\Authenticate as Middleware;
class Authenticate extends Middleware
{
/**
* Get the path the user should be redirected to when they are not authenticated.
*
* #param \Illuminate\Http\Request $request
* #return string|null
*/
protected function redirectTo($request)
{
// both request coming to here i want get middlware name from here
// if('auth:web'){
//redirect to adimin login
// }else{
//redirect to userlogin
//}
if (! $request->expectsJson()) {
return route('admin.login');
}
}
}
You can get a list of all middleware used for the current route using request()->route()->computedMiddleware so your code would be:
<?php
namespace App\Http\Middleware;
use Illuminate\Auth\Middleware\Authenticate as Middleware;
class Authenticate extends Middleware
{
/**
* Get the path the user should be redirected to when they are not authenticated.
*
* #param \Illuminate\Http\Request $request
* #return string|null
*/
protected function redirectTo($request)
{
if ($request->route() && in_array('auth:web', $request->route()->computedMiddleware??[]) {
// redirect to admin login
} else {
// redirect to admin login
}
if (! $request->expectsJson()) {
return route('admin.login');
}
}
}

Laravel redirecting authenticated users to login route

When I try to access any route under the auth middleware I get redirected to a login route.
Route::group(['middleware' => 'auth'], function () {
Route::get('area-cliente/resumo', 'AreaClienteController#resumo')->name('area_cliente_resumo');
});
this is my authenticate middleware
class Authenticate extends Middleware
{
/**
* Get the path the user should be redirected to when they are not authenticated.
*
* #param \Illuminate\Http\Request $request
* #return string
*/
protected function redirectTo($request)
{
if(Auth::guard('cliente')->guest())
{
return route('registrar');
}
return next($request);
}
}
I cannot see where the code that is redirecting to a login route is located and I don't know why either, since the user is apparently logged using the code below;
public function logar(Request $request) {
$credenciais = [
"email" => $request->email,
"senha" => $request->senha
];
$results = auth()->guard('cliente')->login(Cliente::find(1));
if(auth()->guard('cliente')->check()){
//I am able to echo the name of the user
//echo auth()->guard('cliente')->user()->name
return redirect()->route('home');
}
else{
echo "no";
}
}
I've also tried to clean (remove all functions) of the RedirectIfAuthenticated and authenticate middleware but the behavior of the application doesn't change, I still being redirected to a login page on the routes under the auth middleware.

Laravel token authentication

I have a Laravel API (actually a Lumen API) serving a VueJS front-end. The Vue app allows users to login to Google. The Google token is then sent back to the Lumen API, which verifies the token with Google, then verifies that the email address is a valid user. Then it generates a token, stores that in the DB with the user and returns the user object.
I'm not using Passport or jwt-auth or anything like that. So now, how do I go about using the default Auth middleware to verify the token header that the (now logged in) user will be returning with every request? (i.e. is the token in the DB and is it expired?). And is there a way to do this more efficiently, so Laravel caches the valid tokens and doesn't have to hit the DB for every request?
You can use the Authentication Middleware. Pass with your request an api_token. A more detailed answer can be found here.
Add a api_token column to your User table.
$table->string('api_token', 60)->unique();
Have an authentication middleware as such:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
class Authenticate
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #param string|null $guard
* #return mixed
*/
public function handle($request, Closure $next, $guard = null)
{
if (Auth::guard($guard)->guest()) {
if ($request->ajax() || $request->wantsJson()) {
return response('Unauthorized.', 401);
} else {
return redirect()->guest('auth/login');
}
}
return $next($request);
}
}
And use Route Group
Route::group(['middleware' => 'auth:api'], function () {
Route::resource('api/v1/foo/bar', 'API\FooBarController');
}

Make session expiration redirect back to login?

When user logs in and is authenticated, I use Auth::user()->username; to show username of user on dashboard. However, for some reason when session expires the class Auth doesn't seem to work and dashboard page throws error as trying to get property of non-object for Auth::user()->username;. How can I redirect the user back to the login page when he clicks any link or refreshes the page after the session has expired?
I tried the Authenticate.php middleware but it always redirects back to login page,whatever you put the credentials either correct or incorrect.However,when I don't use this middleware it logins the user.Am I missing something?
Route.php
<?php
/*
|--------------------------------------------------------------------------
| Application Routes
|--------------------------------------------------------------------------
|
| Here is where you can register all of the routes for an application.
| It's a breeze. Simply tell Laravel the URIs it should respond to
| and give it the controller to call when that URI is requested.
|
*/
/*
Actions Handled By Resource Controller
Verb Path Action Route Name
GET /photo index photo.index
GET /photo/create create photo.create
POST /photo store photo.store
GET /photo/{photo} show photo.show
GET /photo/{photo}/edit edit photo.edit
PUT/PATCH /photo/{photo} update photo.update
DELETE /photo/{photo} destroy photo.destroy
Adding Additional Routes To Resource Controllers
If it becomes necessary to add additional routes to a resource controller beyond the default resource routes, you should define those routes before your call to Route::resource:
Route::get('photos/popular', 'PhotoController#method');
Route::resource('photos', 'PhotoController');
*/
// Display all SQL executed in Eloquent
// Event::listen('illuminate.query', function($query)
// {
// var_dump($query);
// });
define('ADMIN','admin');
define('SITE','site');
Route::group(['namespace' => ADMIN], function () {
Route::get('/','UserController#showLogin');
});
////////////////////////////////////Routes for backend///////////////////////////////////////////////////
Route::group(['prefix' => ADMIN,'middleware' => 'auth'], function () {
Route::group(['namespace' => ADMIN], function () {
//Route::get('/','EshopController#products');
//sumit routes for user registration
//Route::resource('users','UserController');
Route::get('/users/destroy/{id}','UserController#destroy');
Route::get('UserProf','UserController#userProf');
Route::get('users','UserController#index');
Route::get('/users/create','UserController#create');
Route::get('/users/adminEdit/{id}','UserController#adminEdit');
Route::post('/users/adminUpdate','UserController#adminUpdate');
Route::post('/users/store','UserController#store');
Route::get('/users/edit/{id}','UserController#edit');
Route::post('/users/update/{id}','UserController#update');
//airlines route
Route::get('airlines','AirlinesController#index');
Route::get('/airlines/create','AirlinesController#create');
Route::post('/airlines/store','AirlinesController#store');
Route::get('/airlines/edit/{id}','AirlinesController#edit');
Route::post('/airlines/update','AirlinesController#update');
Route::get('/airlines/destroy/{id}','AirlinesController#destroy');
//end sumit routes
//flight routes
Route::get('flights','FlightController#index');
Route::get('showFlightBook','FlightController#showFlightBook');
Route::get('flights/create','FlightController#create');
Route::post('flights/store','FlightController#store');
Route::get('flights/book','FlightController#book');
Route::get('flights/edit/{id}','FlightController#edit');
Route::post('flights/update','FlightController#update');
Route::get('flights/destroy/{id}','FlightController#destroy');
//Route::resource('flight','FlightController');
//hotels route
Route::get('hotels','HotelsController#index');
Route::get('/hotels/create','HotelsController#create');
Route::post('/hotels/store','HotelsController#store');
Route::get('/hotels/edit/{id}','HotelsController#edit');
Route::post('/hotels/update','HotelsController#update');
Route::get('/hotels/destroy/{id}','HotelsController#destroy');
//end sumit routes
//book-hotel routes
Route::get('hotel-book','HotelBookController#index');
Route::get('showHotelBook','HotelBookController#showHotelBook');
Route::get('hotel-book/create','HotelBookController#create');
Route::post('hotel-book/store','HotelBookController#store');
Route::get('hotel-book/book','HotelBookController#book');
Route::get('hotel-book/edit/{id}','HotelBookController#edit');
Route::post('hotel-book/update','HotelBookController#update');
Route::get('hotel-book/destroy/{id}','HotelBookController#destroy');
//Route::resource('hotel','HotelController');
//close flight routes
//for admin login
//Route::get('initlogin','UserController#lgnPage');
Route::get('login','UserController#showLogin');
// Route::get('privilegeLogin','UserController#privilegeLogin');
// Route::post('privilegeCheck','UserController#privilegeCheck');
Route::post('login','UserController#doLogin');
Route::get('/dashboard','DashController#index');
Route::get('logout','UserController#doLogout');
//user login
//Route::get('userLogin','UserController#showUserLogin');
//Route::post('userLogin','UserController#doUserLogin');
Route::get('/userDashboard','DashController#userIndex');
Route::get('Logout','UserController#doUserLogout');
//password reset
Route::get('forget-pass','UserController#showReset');
//Route::get('home', 'PassResetEmailController#index');
});
});
Route::controllers([
'auth' => 'Auth\AuthController',
'password' => 'Auth\PasswordController',
]);
Authenticate.php:
<?php namespace App\Http\Middleware;
use Closure;
use Illuminate\Contracts\Auth\Guard;
class Authenticate {
/**
* The Guard implementation.
*
* #var Guard
*/
protected $auth;
/**
* Create a new filter instance.
*
* #param Guard $auth
* #return void
*/
public function __construct(Guard $auth)
{
$this->auth = $auth;
}
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if ($this->auth->guest())
{
if ($request->ajax())
{
return response('Unauthorized.', 401);
}
else
{
// return redirect()->guest('auth/login');
return redirect()->guest('/');
}
}
return $next($request);
}
}
All you have to do is just put this constructor at the top of the controller for your dashboard. It seems Laravel has a middleware that handles this already. At least I can confirm from 5.4 and up.
public function __construct()
{
$this->middleware('auth');
}
If the session expires then you can redirect to log in like as
open this file app/Exceptions/Handler.php add this code
public function render($request, Exception $exception)
{
if ($exception instanceof \Illuminate\Session\TokenMismatchException) {
return redirect('/login');
}
return parent::render($request, $exception);
}
If you want a middleware to be run during every HTTP request to your application, simply list the middleware class in the $middleware property of your app/Http/Kernel.php class.
So, to protect every route from being accessed without authentication do this
protected $middleware = [
'Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode',
'Illuminate\Cookie\Middleware\EncryptCookies',
'Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse',
'Illuminate\Session\Middleware\StartSession',
'Illuminate\View\Middleware\ShareErrorsFromSession',
'App\Http\Middleware\VerifyCsrfToken',
'App\Http\Middleware\Authenticate',// add this line according to your namespace
];
it will redirect the user if not logged in. UPDATE Keep in mind that adding auth middleware as global will create redirect loop so avoid it.
Or if you want specific routes to be protected then attach the middleware auth to that route
Route::get('admin/profile', ['middleware' => 'auth', function () {
//
}]);
I think you are not attaching the auth middleware to your routes.
Create a middleware like this
<?php namespace App\Http\Middleware;
use Closure;
use Illuminate\Contracts\Auth\Guard;
class Authenticate
{
/**
* The Guard implementation.
*
* #var Guard
*/
protected $auth;
/**
* Create a new filter instance.
*
* #param Guard $auth
* #return void
*/
public function __construct(Guard $auth)
{
$this->auth = $auth;
}
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if ($this->auth->guest()) {
if ($request->ajax()) {
return response('Unauthorized.', 401);
} else {
return redirect()->guest('login');
}
}
return $next($request);
}
}
Then Group the routes and protect them like this
Route::group(['middleware' => 'auth'], function()
{
Route::get();
Route::get();
Route::get();
Route::get();
}
Offcourse, in the routes you have to specify your links etc, it will only allow the user when he is authenticated and if not then login page will be shown
To make session redirect to your login just add ->middleware('auth') in your router files as shown below I am using laravel 5.3
Ex:
Route::post('controllerName','folderName\fileName#fnNmae')->middleware('auth');
Or visit https://laravel.com/docs/5.3/authentication

Resources