How to handle TokenMismatchException - laravel

I want to handle TokenMismatchException in VerifyCsrfToken.php line 67: exception and then redirect user to login page.
The error occurs when user is logged and and clear browser data (session, history etc) or the user's session expires.
I've already tried the following solution in Handler.php:
if ($e instanceof TokenMismatchException) {
return redirect()->route('welcome');
}
Full Haler.php code:
class Handler extends ExceptionHandler {
protected $dontReport = [
AuthorizationException::class,
HttpException::class,
ModelNotFoundException::class,
ValidationException::class,
];
public function report(Exception $e)
{
parent::report($e);
}
public function render($request, Exception $e)
{
if ($e instanceof TokenMismatchException) {
return redirect()->route('welcome');
}
return parent::render($request, $e);
}
}
I've also tried to return the status code and then try to handle it, but when I return status code by $e->getStatusCode() it does not return any value. Therefore I don't know how to handle this exception.
Any reasonable suggestions will be appreciated.

If you want to handle the TokenMismatchException from your app/Exceptions/Handlerthen you need to remove it from the $dontReport array in your Handler class
/**
* A list of the exception types that should not be reported.
*
* #var array
*/
protected $dontReport = [
\Illuminate\Auth\AuthenticationException::class,
\Illuminate\Auth\Access\AuthorizationException::class,
\Symfony\Component\HttpKernel\Exception\HttpException::class,
\Illuminate\Database\Eloquent\ModelNotFoundException::class,
// \Illuminate\Session\TokenMismatchException::class, --> delete this line
\Illuminate\Validation\ValidationException::class,
];
Although, I would warn you that redirecting the user to the login page on this exception isn't really the right way to handle this.
A TokenMismatchException is thrown when the csrf token doesn't match. CSRF protection is for both logged in users and guests. So, redirecting to the login page is not really a solution to this problem.

Related

Laravel localization for custom error pages

I have custom error pages e.g. resources/views/errors/404.blade.php everything is working just fine but the localization is not working for error pages. If I change website language the error pages still show in default language I tried in many way but its not working, Can anyone please help me make this work thanks in advance.
I try to make it work via exception handler but don't know how to do that. I can apply language middleware is someone can tell me where is default routes for error pages.
You can also redirect to other pages in App\Exceptions\Handler.php. You can also assign using App::setLocale(). Like this:
public function render($request, Throwable $exception)
{
App::setLocale('en_GB');
/** #var \Symfony\Component\HttpKernel\Throwable $e */
$e = $exception;
$statusCode = $e->getStatusCode();
return $this->isHttpException($exception) && $statusCode == 404 ?
response()->view('frontend.pages.404') :
parent::render($request, $exception);
}
Open app/exceptions/handler.php
find render function paste here
don't for get import this trait
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
public function render($request, Exception $e)
{
if($request->hasCookie('language')) {
// Get cookie
$cookie = $request->cookie('language');
// Check if cookie is already decrypted if not decrypt
$cookie = strlen($cookie) > 2 ? decrypt($cookie) : $cookie;
// Set locale
app()->setLocale($cookie);
}
if($e instanceof NotFoundHttpException) {
return response()->view('errors.404', [], 404);
}
return parent::render($request, $e);
}
thank you guys a little discussion with you guys fixed my problem I get the session's locale value in exception handler that worked for me I am answering it may be can help some one else. Below are the thing I did in App/Exception/Handler.php
use Session;
public function render($request, Throwable $exception)
{
app()->setLocale(Session::get('locale'));
return parent::render($request, $exception);
}
also i moved
\Illuminate\Session\Middleware\StartSession::class,
from web group to global group in kernal.php

how to change "CSRF token mismatch" message?

I'm using larvel 8 and want to change message of "CSRF token mismatch" when using ajax post. I created a closure and passed it to the renderable method on the App\Exceptions\Handler class, but the previews message appears.
This is my code:
use Illuminate\Session\TokenMismatchException;
class Handler extends ExceptionHandler
{
protected $dontReport = [
//
];
protected $dontFlash = [
'password',
'password_confirmation',
];
public function register()
{
$this->renderable(function (TokenMismatchException $e, $request) {
return $request->expectsJson()
? response()->json(['message' => 'A new message...'], 419)
: redirect()->guest(route('login'));
});
}
To modify error message on TokenMismatchException both for web-pages and ajax-requests
It would be better to overload prepareException() method in application exception handler:
protected function prepareException(Exception $e)
{
if ($e instanceof TokenMismatchException) {
$e = new HttpException(419, __('exception.csrf_token_mismatch'), $e);
}
return parent::prepareException($e);
}
So you can create translation file and modify message by language files. For example create resources/lang/en/exception.php with content below:
<?php
return [
'csrf_token_mismatch' => 'CSRF token mismatch. Please, refresh page (CTRL+R) and try again.',
];
thanks to everyone who contributed, I found the solution.
Due to laravel change the TokenMismatchException to HttpException in the function prepareException in the Illuminate\Foundation\Exceptions\Handler class(parent of Handler class), we cannot render the TokenMismatchException.
protected function prepareException(Throwable $e)
{
if ($e instanceof ModelNotFoundException) {
$e = new NotFoundHttpException($e->getMessage(), $e);
} elseif ($e instanceof AuthorizationException) {
$e = new AccessDeniedHttpException($e->getMessage(), $e);
} elseif ($e instanceof TokenMismatchException) {
$e = new HttpException(419, $e->getMessage(), $e);
} elseif ($e instanceof SuspiciousOperationException) {
$e = new NotFoundHttpException('Bad hostname provided.', $e);
} elseif ($e instanceof RecordsNotFoundException) {
$e = new NotFoundHttpException('Not found.', $e);
}
return $e;
}
I modify my renderable method as below and now I can catch the TokenMismatchException:
$this->renderable(function (HttpException $e, $request) {
if ($e->getPrevious() instanceof TokenMismatchException) {
return $request->expectsJson()
? response()->json(['message' =>'Your new message ...', 419)
: redirect()->guest(route('login'));
}
});
If you want to change the error message or the page that is shown when CSRF token mismatch happen
Run this command: php artisan vendor:publish --tag=laravel-errors
It will publish your default (vendor) exceptions page to resources/views/errors/
From there, edit resources/views/errors/419.blade.php with html that you would like to show when CSRF verification error happen.
References: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
419 Page Expired (Laravel Framework)
Used by the Laravel Framework when a CSRF Token is missing or expired.
If you want to allow ajax requests to bypass CSRF token verification
Reference: https://laravel.com/docs/8.x/csrf#csrf-excluding-uris
Edit your VerifyCsrfToken middleware (location: app/Http/Middleware/VerifyCsrfToken.php), add:
class VerifyCsrfToken extends Middleware
{
/**
* The URIs that should be excluded from CSRF verification.
*
* #var array
*/
protected $except = [
'stripe/*',
'http://example.com/foo/bar',
'http://example.com/foo/*',
];
}

How to redirect users to another page with session messages while error occuring

I want to redirect users to admin page when any error occuring about the thujohn/twitter package. It throws Runtimeexception..
So I add couple code handler.php
public function render($request, Exception $exception)
{
if ($exception instanceof \RuntimeException) {
return redirect()->route('admin.panel')
->with('message', 'Please try again later..')
->with('message_type','warning');
} else {
return parent::render($request, $exception);
}
}
But when I say that, it redirects user at all exceptions even at 404 errors or Trying to get property of non-object erros.. How can I fix this ? I want to redirect user for just relevant error
Or is there any way to do redirect user with condition like below.
if($exception->code == 436){
// it says member has protected access. I can't use it code property outside of the exception class
return redirect()->route('admin.panel')
->with('message', 'Specific error message')
->with('message_type','warning');
}
First of all go to Exceptions/Handler.php.
Here you can develope your exeptions.
This is my example on QueryException (you need to find by dd() your exception and its code):
use Illuminate\Database\QueryException; // REMEMBER TO USE PROPER INSTANCE
public function render($request, Exception $exception)
{
//dd($exception); <-- here you can catch and check type od exception
switch(true) {
case $exception instanceof QueryException:
if($exception->getCode() == '23000') {
return redirect(
route('get.dashboard.index')
)->with('warning', 'No record in database);
}
break;
}
}

Different Response(JSON and webpage) in API and Website for Laravel 404 and 500?

I want to show the different response for API and website. In api response I want to show json response with 404 and 500 for type of exception mainly for routes.
If a user try to request a route and route not found I want to show a response in json response for API and webpage for website.
I know and try the code into app/Exceptions/Handler.php
public function render($request, Exception $exception)
{
if ($exception instanceof NotFoundHttpException) {
if ($request->expectsJson()) {
return response()->json(['error' => 'Not Found'], 404);
}
return response()->view('404', [], 404);
}
return parent::render($request, $exception);
}
https://laravel.com/docs/5.4/errors#http-exceptions
but failed can anybody help me how can I set different responses for error pages.
Expects JSON is about headers, i do not like that solution for API errors, you can access the API through a browser. My solution is most of the times to filter by the URL route, because it starts with "api/...", which can be done like so $request->is('api/*').
If you have your routes that are not prefixes with /api, then this will not work. Change the logic to fit with your own structure.
public function render($request, Exception $exception)
{
if ($exception instanceof NotFoundHttpException) {
if ($request->is('api/*')) {
return response()->json(['error' => 'Not Found'], 404);
}
return response()->view('404', [], 404);
}
return parent::render($request, $exception);
}
Just wanted to add an alternative to the above answers, which all seem to work as well.
After having the same problem and digging deeper, I took a slightly different approach:
your exception handle calls parent::render(...). If you look into that function, it will render a json response if your request indicates that it wantsJson() [see hints how that works here]
now, to turn all responses (including exceptions) to json I used the Jsonify Middleware idea from here, but applied it to the api MiddlewareGroup, which is by default assigned to RouteServiceProvider::mapApiRoutes()
Here is one way to implement it (very similar to referenced answer from above):
Create the file app/Http/Middleware/Jsonify.php
<?php
namespace App\Http\Middleware;
use Closure;
class Jsonify
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if ( $request->is('api/*') ) {
$request->headers->set('Accept', 'application/json');
}
return $next($request);
}
}
Add the middleware reference to your $routeMiddleware table of your app/Http/Kernel.php file:
protected $routeMiddleware = [
...
'jsonify' => \App\Http\Middleware\Jsonify::class,
...
];
In that same Kernel file, add the jsonify name to the api group:
protected $middlewareGroups = [
...
'api' => [
'jsonify',
'throttle:60,1',
'bindings',
],
...
];
Result is that the middleware gets loaded for any requests that fall into the 'api' group. If the request url begins with api/ (which is slightly redundant I think) then the header gets added by the Jsonify Middleware. This will tell the ExceptionHandler::render() that we want a json output.
No need to hustle again for Laravel upgrade. You just need to define this method in the routes/api.php
Route::fallback(function(){
return response()->json(['message' => 'Not Found!'], 404);
});
I'm using Laravel 5.5.28, and am adding this in app/Exceptions/Handler.php
public function render($request, Exception $exception)
{
// Give detailed stacktrace error info if APP_DEBUG is true in the .env
if ($request->wantsJson()) {
// Return reasonable response if trying to, for instance, delete nonexistent resource id.
if ($exception instanceof \Illuminate\Database\Eloquent\ModelNotFoundException) {
return response()->json(['data' => 'Resource not found'], 404);
}
if ($_ENV['APP_DEBUG'] == 'false') {
return response()->json(['error' => 'Unknown error'], 400);
}
}
return parent::render($request, $exception);
}
This expects that your API calls will be having a header with key Accept and value application/json.
Then a nonexistent web route returns the expected
Sorry, the page you are looking for could not be found
and a nonexistent API resource returns a JSON 404 payload.
Found the info here.
You could combine this with the answer looking for the instance of NotFoundHttpException to catch the 500. I imagine, however, that the stack trace would be preferred.
finally found this for 9.x
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Register the exception handling callbacks for the application.
*
* #return void
*/
public function register()
{
$this->renderable(function (NotFoundHttpException $e, $request) {
if ($request->is('api/*')) {
return response()->json([
'message' => 'Record not found.'
], 404);
}
});
}
source: https://laravel.com/docs/9.x/errors#rendering-exceptions
try this.
public function render($request, Exception $exception)
{
if ($request->ajax()) {
return \Response::json([
'success' => false,
'message' => $exception->getMessage(),
], $exception->getCode());
} else {
return parent::render($request, $exception);
}
}

Laravel: how to prevent a MethodNotAllowedHttpException in RouteCollection.php when someone access a post-route without giving post parameters?

Laravel: how to prevent a MethodNotAllowedHttpException in RouteCollection.php when someone access a post-route without giving post parameters?
When i define a post route its only accessable by a post request. If i call the url normaly in a browser, it throws an exception. how to prevent this?
Anyone can do a request to any of your routes using a method that isn't allowed. You can make a view for the exception in the resources/views/errors folder. Laravel comes with an error page for the HTTP status code 503 (Service Unavailable Error) by default.
The HTTP status code for the MethodNotAllowedHttpException is 405, so making a view called 405.blade.php will cause Laravel to present that view instead of the MethodNotAllowedHttpException message.
This should do the job
This will catch all errors like, MethodNotAllowed or 404(page not found) or ModelNotFound or TokenMissMatch and redirect to any route you desire with proper error!
Handler.php
public function render($request, Exception $e){
if ($e instanceof ForbiddenException) {
return redirect()->route('name')->withErrors(['error' => $e->getMessage()]);
}
return parent::render($request, $e);
}
Or just for Method not allowed
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
if ($e instanceof MethodNotAllowedHttpException) {
return redirect()->route('name')->withErrors(['error' => $e->getMessage()]);
}
Use Route::any() or Route::match()
Example:
Route::match(['get', 'post'], '/', function () {
//
});
Route::any('foo', function () {
//
});
I tried the match and any methods but they were not what I needed.
This is what I did:
1- Added the default error handler in the Base controller.
2- Called the error manually for each forbidden HTTP METHOD.
Then, on the BaseController:
adding a file "405.blade.php" into your "resources/views/errors" will prevent the MethodNotAllowedHttpException page from showing up again.
For laravel 9, i solved it this way, you can change the exception to be caught. Make sure to include it at the top:
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
And:
Handler.php
/**
* Register the exception handling callbacks for the application.
*
* #return void
*/
public function register()
{
$this->reportable(function (Throwable $e) {
//
});
$this->renderable(function (MethodNotAllowedHttpException $e, $request) {
return abort(404, "page not found");
});
}

Resources