Lavavel 5: Error page middlewares are not accessible - laravel

I'm using Laravel 5.0 and getting one issue. My site is working fine when there is no error like 404, 500 etc. But when there is error on my site then it render error page but some middlewares are not loading.
My site has two parts frontend and backend. Both parts have diff-diff middlewares which I'm using. I'm using some number of custom middlewares, some of them I'm using on frontend and some of them using on backend. But when there is any error on site then middlewares are not working which are loaded in $routeMiddleware array in Kernel.php file.
I research number of articles but all gives me same solution to load all required middlewares in $middleware, which seems not fine to me. Because in $middleware array all middlewares are loading for both fronend and backend sections. But I need to load spacial middlewares only single section. How could I implement this.
This is my kernel.php file
class Kernel extends HttpKernel {
/**
* The application's global HTTP middleware stack.
*
* #var array
*/
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',
];
/**
* The application's route middleware.
*
* #var array
*/
protected $routeMiddleware = [
'auth' => 'App\Http\Middleware\Authenticate',
'auth.frontend' => 'App\Http\Middleware\FrontendAuthenticate',
'auth.basic' => 'Illuminate\Auth\Middleware\AuthenticateWithBasicAuth',
];
}
And this is my route.php file code
Route::group(['middleware' => 'auth.frontend'], function() {
Route::get('/home', ['as' => 'home', 'uses' => 'HomeController#index']);
Route::get('/inner_page', ['as' => 'inner_page', 'uses' => 'HomeController#inner_page']);
});
How this is possible, please help me on this issue.

As explained in documentation, you must define a terminate($request, $response) method to run the middle-ware after the response is ready to be sent to the browser. The terminate($request, $response) method should receive both the request and the response.
then you can either add your middle-ware to the global or route middle-ware arrays.
example :
<?php
namespace App\Http\Middleware;
use Closure;
class FrontendAuthenticate
{
public function handle($request, Closure $next)
{
return $next($request);
}
public function terminate($request, $response)
{
// Check if the $response is an error and then do stuff
}
}

Related

Where to switch to other service depending on logged user field?

In laravel 9 app I try to switch to different service
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* #return void
*/
public function register()
{
if ($this->app->environment('local')) {
$this->app->register(\Laravel\Telescope\TelescopeServiceProvider::class);
$this->app->register(TelescopeServiceProvider::class);
$this->app->register(\Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class);
}
// Error pointing at this line
$loggedUser = Auth::user();
if( $loggedUser->membership_mark === 'G') { // G=>Gold Membership
$this->app->bind('App\Interfaces\PageMembershipMarkStrategyInterface',
'App\Implementations\PageGoldMemberStrategy');
}
}
But I got error :
Target class [hash] does not exist. in...
Looks like register method is not proper place for app->bind when I need to get logged user.
But where have I to make this binding? In custom service provider? Im middleware ?
MODIFIED BLOCK :
I create a middleware with command :
php artisan make:middleware SetPageMembershipMarkService
and in app/Http/Kernel.php I added it :
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
// I added this line :
\App\Http\Middleware\SetPageMembershipMarkService::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
But I got error :
Illuminate\Contracts\Container\BindingResolutionException: Target [App\Interfaces\PageMembershipMarkStrategyInterface] is not instantiable while building [App\Http\Controllers\InformOnPageInStockController]....
I am not sure in which place of Kernel.php I have to add ref to PageMembershipMarkStrategyInterface ?
checking log it appears that my SetPageMembershipMarkService is not triggered before error...
MODIFIED BLOCK 2:
In file app/Http/Kernel.php I added my Middleware reference:
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
// I set it AFTER auth
'setPageMembership' =>\App\Http\Middleware\SetPageMembershipMarkService::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminat`enter code here`e\Auth\Middleware\EnsureEmailIsVerified::class,
];
abd in routes/api.php :
Route::middleware('auth:api')->prefix('v1') /*->name('api.v1')*/ ->group(function () {
Route::get('/user', function (Request $request) {
return $request->user();
});
...
Route::middleware(['setPageMembership'])->group(function () {
Route::resource('/inform-on-page-in-stock', InformOnPageInStockController::class)->except(['edit', 'add']);
});
and I still got error :
Illuminate\Contracts\Container\BindingResolutionException: Target [App\Interfaces\InformOnPageInStockStrategyInterface] is not instantiable while building [App\Http\Controllers\InformOnPageInStockController].
Checking logs I do not find log message I set in SetPageMembershipMarkService middleware, so looks like it does
not set before my controllers...
What is wrong ?
You mentioned using of RouteServiceProvider. Which is correct syntax ?
File app/Http/Middleware/SetPageMembershipMarkService.php has lines :
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Auth;
class SetPageMembershipMarkService
{
/**
* 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)
{
$loggedUser = Auth::user();
\Log::info(varDump($loggedUser, ' -1 SetPageMembershipMarkService $loggedUser::'));
$membershipMark = $loggedUser->membership_mark;
if( $membershipMark === 'S') { // G=>Silver Membership
app()->bind('App\Interfaces\InformOnPageInStockStrategyInterface',
'App\Implementations\HasNoInformOnPageInStockStrategy');
}
if( $membershipMark === 'G') { // G=>Gold Membership
app()->bind('App\Interfaces\InformOnPageInStockStrategyInterface',
'App\Implementations\InformOnPageInStockGoldMembersStrategy');
}
return $next($request);
}
}
But in log file I do not see any log message I have in this Middleware...
Thanks!
You will not be able to get Auth:user() data in Service Provder. Because service provider runs before Laravel sets the Auth data, you'll have to use some hacky methods to handle it.
I think better ways to handle data related to auth would be on middleware and not in Service Provider.
Additional explanations can be found on this questions answer Laravel - How to get current user in AppServiceProvider
EDIT for modifed block
Your middleware registration is wrong.
$routeMiddleware this array is used to register a custom middleware which you gonna use and define manually
For example:
protected $routeMiddleware = [
.....
// I added this line :
'setPageMembership' => \App\Http\Middleware\SetPageMembershipMarkService::class,
Then you gonna use this middleware in your route groups like:
Route::middleware(['auth', 'setPageMembership'])->group(function () {
Route::get('test', [TestController::class, 'test']);
});
If you want the middleware to run for all the routes it is better to add it in the $middleware array or if you want in the certain route group (web/api) then in the $middlewareGroups individual web and api array.
Adding the middleware in RouteServiceProvder also works if you want every route to have the middleware.
However, for your use case, authentication is required before binding. So, you will need auth middleware to fire first before you perform those bind actions. So adding the middleware to your route file like I showed above will be enough for your use case.

NotFoundHttpException in Laravel middleware and controller

I am new to laravel and learning basics from https://www.tutorialspoint.com/laravel/laravel_middleware.htm
After installation I have created RoleMiddleware and TestController.
RoleMiddleware.php
<?php
namespace App\Http\Middleware;
use Closure;
class RoleMiddleware
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next, $role)
{
echo "Role: ".$role;
return $next($request);
}
}
and TestController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
class TestController extends Controller
{
//
public function index(){
echo "<br>Test Controller.";
}
}
and app\http\ routes.php
Route::get('/role',[
'middleware' => 'Role:editor',
'uses' => 'TestController#index',
]);
and in Kernel.php
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware \AuthenticateWithBasicAuth::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'Age' => \App\Http\Middleware\AgeMiddleware::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'Role' => \App\Http\Middleware\RoleMiddleware::class,
'terminate' => \App\Http\Middleware\TerminateMiddleware::class,
];
After written all these code I have executed this using http://localhost:8000/role but it gives NotFoundHttpException, Please help me to solve this issue..
First of all you are using some older version of Laravel, as your routes are listed in app\http\routes.php file because in newer versions, it is actually routes\web.php file:
Then you made a mistake in your route:
Route::get('/role', ['middleware' => 'Role:editor', 'uses' => ]);
http routes must have a callback/closure, whether it is custom function or using a controller... Try with following basic options:
Route::get('/role', function() { return "Working"; });
Also check whether your route is not listed as excepted route
If its working, then play with middlewares!!!
i cant send a comment to ask for hole error ( bcs of my reputation )
but i write middle ware in this way . maybe its help you :
first make sure that $role is string or put it in json_encode ( on RoleMiddleware.php )
==> echo "Role: ".json_encode($role);
then change your rout to below :
Route::get('/role', 'TestController#index')->middleware('Role');
send back to me the result

Web middleware does not get executed (Laravel 5.5)

I'm trying to create a website with multiple languages (with Laravel 5.5). Therefore I follow the tutorial https://mydnic.be/post/how-to-build-an-efficient-and-seo-friendly-multilingual-architecture-for-your-laravel-application, but somehow I have troubles with the language middleware part:
// BeforeLanguage.php
namespace App\Http\Middleware;
use Closure;
class BeforeLanguage
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
// Check if the first segment matches a language code
if (!array_key_exists($request->segment(1), config('translatable.locales')) ) {
// Store segments in array
$segments = $request->segments();
// Set the default language code as the first segment
$segments = array_prepend($segments, config('app.fallback_locale'));
// Redirect to the correct url
return redirect()->to(implode('/', $segments));
}
return $next($request);
}
}
It works if I open a URL with the identifier for the language, e. g. http://ps.dev/de/app/login but I get "Sorry, the page you are looking for could not be found." if I try to open the page without the language identifier in the URI, e. g. http://ps.dev/app/login.
But here I would expect that the middleware adds the language segment to the URI. Any idea what could go wrong?
Below I would like to provide some additional information.
web.php
Route::prefix('app')->group(function () {
// Authentication routes
Route::get('login', 'SessionsController#create')->name('login');
// ...
});
Kernel.php
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\BeforeLanguage::class,
\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,
],
'api' => [
'throttle:60,1',
'bindings',
],
];
According to the Laravel documentation page the middlewares assigned to the middleware group web get executed by default for any route defined inside web.php. (Out of the box, the web middleware group is automatically applied to your routes/web.php file by the RouteServiceProvider.)
RouteServiceProvider.php
protected function mapWebRoutes()
{
$locale = Request::segment(1);
Route::prefix($locale)
->middleware('web')
->namespace($this->namespace)
->group(base_path('routes/web.php'));
}
I think you had chosen a hard way something like "Reinvent the Wheel!" ;)
Although It's your decision and respectful, as a suggestion try this nice package for localization in Laravel :D
The reason why it is sending to the 404 is that the url doesn't exist. In your route file, you have not created the paths for language. So ultimately, if your
middleware works it will send to the non-existing route path, which will be 404.
Add an identifier for the language :-
Route::get('{lang}/login', 'SessionsController#create')->name('login');
Check you routes by the following artisan command in cmd
php artisan route:list

Laravel 5.4 - Prefixing and applying two middlewares for a multi-tenant application's routes

I'd like to group /account/{account_id} as well as protect every route inside it with the auth middleware and a middleware that will check if the logged user has access to this account. Unfortunately it's not working.
Here is my code
web.php:
Route::group(['middleware' => 'auth'], function () {
Route::group(['prefix' => 'account/{account}', 'middleware' => 'userHasPermissionForAccount'], function() {
Route::group(['prefix' => 'posts'], function () {
Route::get('{post}', 'PostsController#index')->where([
'post' => '\d+'
]);
});
// more routes here...
});
});
app/http/kernel.php
// ...
protected $routeMiddleware = [
// ...
'userHasPermissionForAccount' => \App\Http\Middleware\UserCanAccessContent::class,
];
It don't even triggers the code inside my custom middleware and I don't understand why.
I would use policies (gates), since you can use policy as middleware.
$ php artisan make:policy AcccountPolicy
Register the policy in AuthServiceProvider:
/**
* The policy mappings for the application.
*
* #var array
*/
protected $policies = [
Account::class => AccountPolicy::class, //Account is model, remember to import!
];
Inside policy file (app/policies/AccountPolicy.php), create method lets say "manage"
/**
* Determine if ....
*
* #param \App\User $current
* #param \App\Account $account
* #return bool
*/
public function manage(User $current, Account $account)
{
//return some logic here to check if $current is part of $account
}
And then use this policy as middleware:
Route::group(['prefix' => 'account/{account}', 'middleware' => 'can:manage,account'], function...

Customising token response Laravel Passport

I am working on an API at the moment and have hit a brick wall. I am using Passport with the 'Password' grant type.
I want to return the user information with the access tokens, however, I am not sure how to.
Which class could I implement, edit or extend to get this?.
I would like this to be returned:
{
"token_type": "Bearer",
"expires_in": 31536000,
"access_token": "lalalalalal",
"refresh_token": "lalalallala",
"user": {
"username": "a username",
"user_type": "admin"
}
}
Thanks in advance.
The instructions on how to do this are hinted in the BearerTokenResponse class (part of the league/oauth2-server package).
Tested on Laravel 5.7.
1. Extend the BearerTokenResponse class, add the extra params you need in the response.
namespace App\Auth;
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
class BearerTokenResponse extends \League\OAuth2\Server\ResponseTypes\BearerTokenResponse
{
/**
* Add custom fields to your Bearer Token response here, then override
* AuthorizationServer::getResponseType() to pull in your version of
* this class rather than the default.
*
* #param AccessTokenEntityInterface $accessToken
*
* #return array
*/
protected function getExtraParams(AccessTokenEntityInterface $accessToken): array
{
return [
'user_id' => $this->accessToken->getUserIdentifier(),
];
}
}
2. Create your own PassportServiceProvider class and override the makeAuthorizationServer() method in order to pass in your own BearerTokenResponse class.
namespace App\Providers;
use App\Auth\BearerTokenResponse;
use Laravel\Passport\Bridge;
use League\OAuth2\Server\AuthorizationServer;
class PassportServiceProvider extends \Laravel\Passport\PassportServiceProvider
{
/**
* Make the authorization service instance.
*
* #return \League\OAuth2\Server\AuthorizationServer
*/
public function makeAuthorizationServer()
{
return new AuthorizationServer(
$this->app->make(Bridge\ClientRepository::class),
$this->app->make(Bridge\AccessTokenRepository::class),
$this->app->make(Bridge\ScopeRepository::class),
$this->makeCryptKey('private'),
app('encrypter')->getKey(),
new BearerTokenResponse() // <-- The class you created above
);
}
}
3. Add your provider to the providers array in config/app.php
/*
* Application Service Providers...
*/
App\Providers\PassportServiceProvider::class,
4. Exclude the passport package from laravel auto-discovery in composer.json
This stops the default PassportServiceProvider class from being loaded.
"extra": {
"laravel": {
"dont-discover": [
"laravel/passport"
]
}
},
Then run composer install.
Two steps.
1. Add a new route in your routes file.
// routes/api.php
Route::post('oauth/token', 'AuthController#auth');
Keep in mind this will change the route for getting the token from /oauth/token to /api/oauth/token.
2. Add the controller method.
<?php
// app/Http/Controllers/AuthController.php
namespace App\Http\Controllers;
use App\User;
use Psr\Http\Message\ServerRequestInterface;
use \Laravel\Passport\Http\Controllers\AccessTokenController;
class AuthController extends AccessTokenController
{
public function auth(ServerRequestInterface $request)
{
$tokenResponse = parent::issueToken($request);
$token = $tokenResponse->getContent();
// $tokenInfo will contain the usual Laravel Passort token response.
$tokenInfo = json_decode($token, true);
// Then we just add the user to the response before returning it.
$username = $request->getParsedBody()['username'];
$user = User::whereEmail($username)->first();
$tokenInfo = collect($tokenInfo);
$tokenInfo->put('user', $user);
return $tokenInfo;
}
}
Im using Multi-Auth with passport, so the previous answers didn't help me.
After hours of "googling" I found this answer (after-) middleware.
My middleware basically gets the result of Passport auth, checks if there is an Bearer inside and append more data to the content.
<?php
namespace App\Http\Middleware;
use Closure;
class AppendTokenResponse
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$response = $next($request);
$content = json_decode($response->content(), true);
if (!empty($content['access_token'])) {
$content['moredata'] = 'some data';
$response->setContent($content);
}
return $response;
}
}
Now put the new Middleware in $routemiddleware at App/Http/Kernel.php
/**
* The application's route middleware.
*
* These middleware may be assigned to groups or used individually.
*
* #var array
*/
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'cors' => \App\Http\Middleware\Cors::class,
'multiauth' => \SMartins\PassportMultiauth\Http\Middleware\MultiAuthenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
'oauth.providers' => \SMartins\PassportMultiauth\Http\Middleware\AddCustomProvider::class,
'append_auth' =>\App\Http\Middleware\AppendTokenResponse::class,
];
Then just register this middleware to Passport Routes in Providers/AuthServiceProvider.php
With Multiauth:
Route::group(['middleware' => ['oauth.providers','append_auth']], function () {
Passport::routes(function ($router) {
return $router->forAccessTokens();
});
});
I believe regular passport should be (not tested):
Route::group(['middleware' => ['append_auth']], function () {
Passport::routes();
});
Another better answer from the web
Custom Laravel Passport BearerTokenResponse
https://gist.github.com/messi89/489473c053e3ea8d9e034b0032effb1d
To add custom claims to your Passport token, here is a gist using Passport 8 with Laravel 6
https://gist.github.com/onamfc/0422da15743918e653888441ba6226ca

Resources