Laravel passport change header authentication - laravel

I am using Laravel passport and it requires to send in every request the header Authentication to be sent.
Is it possible to change the name of the header to X-Access-Token?
I saw passport uses the package
League\OAuth2\Server\AuthorizationValidators;
method:
/**
* {#inheritdoc}
*/
public function validateAuthorization(ServerRequestInterface $request)
{
dd($request);
if ($request->hasHeader('authorization') === false) {
throw OAuthServerException::accessDenied('Missing "Authorization" header');
}
$header = $request->getHeader('authorization');
$jwt = trim(preg_replace('/^(?:\s+)?Bearer\s/', '', $header[0]));
I tried to change here but seems the validation of the headers happen before this method.

There are many fundamental pieces of code that rely on the existence of the authorization header.
You could roll your own if you felt so inclined.
Note also that authorization is a web standard request header. X-Access-Token is a response header pattern.
*Edit**
Given our conversation below, you can use Middleware and Middleware priority to dictate which runs first, observe requests that have an X-Access-Token and use addHeader to convert the value of that header to authorization:
php artisan make:middleware AuthorizationToolMiddleware
Then in the handle function:
public function handle($request, Closure $next)
{
$request->headers->set('Authorization', $request->headers->get('X-Access-Token'));
return $next($request);
}
This middleware should execute before other middleware in order to ensure the headers are set by the time that passport handles the request.

For Laravel 5.8 you'd have to force your custom middleware to always be on top of the call chain
So in your app\kernel.php add this -
protected $middlewarePriority = [
\App\Http\Middleware\AuthorizationToolMiddleware::class,
];

Related

Verify access token with Database in laravel

In my laravel project, I am allowing user to generate a token which will be stored in tenants Database. Now when an API is called ,at that time I want to verify this token with the token stored in DB. How to do that ?? How can I achieve this using middleware ?? Please advise
You could verify it in middleware as follows:
create a middleware file with command php artisan make:middleware EnsureTokenIsValid
Now go to app/Http/Middleware and find EnsureTokenIsValid middleware. Then, on handle method you should implement your logic with something like:
public function handle($request, Closure $next)
{
// you have to get token from database below line is just an example
$tokenInDatabase = TenanentModel::find(1);
if ($request->input('token') !== $tokenInDatabase) {
// if token not match redirect to home page
// or implement your logic
return redirect('home');
}
return $next($request);
}
Next, you need to register your middleware on app/Http/Kernel.php as mentioned in https://laravel.com/docs/9.x/middleware#assigning-middleware-to-routes. Example:
'ensureTokenIsValid' => \App\Http\Middleware\EnsureTokenIsValid::class,
Next, add to the route. Example:
Route::get('/profile', function () {
//
})->middleware('ensureTokenIsValid');
You could find more details about this on: https://laravel.com/docs/9.x/middleware#defining-middleware

Route type delete does not work in Laravel

I have following route and works
Route::post("delete-role", [RoleApiController::class, "Remove"]);
I tested it through postman like this
http://localhost/delete-role?api_token=hcvhjbhjb12khjbjhc876
Now, if I change above route and convert to type delete..
Route::delete("delete-role/{role_id}", [RoleApiController::class, "Remove"]);
it does not work. I get below error. It seems to be the reason that the api_token is missing.
I get same error when trying to update route like below
Route::delete("delete-role/{role_id}/{api_token}", [RoleApiController::class, "Remove"]);
You have to set header of your request as:
"Accept": "application/json"
in postman.
If you don't set the required header for api, Laravel Passport can't understand request as an API client and so it will redirect to a /login page for the web.
Or you can set a middleware to check it in code:
public function handle($request, Closure $next)
{
if(!in_array($request->headers->get('accept'), ['application/json', 'Application/Json']))
return response()->json(['message' => 'Unauthenticated.'], 401);
return $next($request);
}
You have an incomplete details. but I see few issues here.
You seem to be using web routes for your API requests which is a bad set-up
You do not have a route with login name.
based on the error you posted, your request seems to successfully destroyed the token and logged you out, then called the middleware App\Http\Middleware\Authenticate which supposed to redirect your request to login route which does not exist and the reason you are getting that error.
You can see from that Authenticate middleware it will supposed to redirect you to login route for unauthenticated request. thats why you need to use the api routes so you can handle the response manually
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->expectsJson()) {
return route('login');
}
}
}
Also, I'm not so sure about this, but the reason you are not getting the same issue with your POST request is probably because your POST request does not call the Authenticate middleware or whatever in your routes file or class function that calls the authenticate middleware.
But again, just use api routes if you don't want un-authenticated request to automatically redirect your request to login routes which does not exist in your application
The problem is that he doesn't define route ('login'),
add in Exceptions/Handler.php
$this->renderable(function (AuthenticationException $e, $request) {
if ($request->is('api/*')) {
return response()->json(['meassge' => 'Unauthenticated']);
}
});
Then you should use Passport Or Sanctum for auth with Api,
Continue from here https://laravel.com/docs/9.x/passport
Probably, this thread could help you. Route [login] not defined
(OR)
You need to setup auth scaffolding to get login route defined.
Important: your project will be overridden if you setup auth scaffold.
So, I would only recommend doing this if it is a new project or a testing app.
See this for detail doc but install Laravel Breeze would be suffice.
It Appears you have called route('login') without having defined it in your routes, Please remove route('login') from your code or define it in your routes. eg::
Route::get('login', [YourController::class, 'methodName'])->name('login');

"GET method is not supported for this route" even though it's a POST route

I have a POST route in my Laravel application:
Route::post('/register-direct', 'Auth\RegisterController#direct')->name('register.direct');
Currently the method doesn't do anything but try to log the request:
public function direct(Request $request) {
logger()->info($request->all());
}
Since this route should be accessible from outside the domain, I disabled CSRF protection for it:
class VerifyCsrfToken extends Middleware
{
/**
* Indicates whether the XSRF-TOKEN cookie should be set on the response.
*
* #var bool
*/
protected $addHttpCookie = true;
/**
* The URIs that should be excluded from CSRF verification.
*
* #var array
*/
protected $except = [
'register-direct'
];
}
However, the strangest thing is happening. Even though it's a POST route, when I try to send the request from Postman to my remote site, I get the error:
The GET method is not supported for this route. Supported methods: POST.
From what I see, it is doing some sort of redirect to /register-direct as a GET route for some reason. The request never reaches the appropriate controller method (since the logging in the method never happens).
I suspect some middleware is the culprit, but the only middleware on this controller is the guest() middleware. And when I disable this middleware, it doesn't change anything.
Additional info:
there is no other route with the same name or URI
when I send the POST request to localhost (instead of to my remote site) it works correctly
the same error appears in Postman and if I try to submit this request from another domain
I have tried moving the route from web.php to api.php and nothing changed
I have other routes in api.php that accept remote requests and work just fine

How does Laravel know whether a middleware should be run after the request handled?

I read the source code, and there is only a pipeline which reads all middlewares as an array. These middlewares should be run before the request dispatchToRouter.
return (new Pipeline($this->app))
->send($request)
->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
->then($this->dispatchToRouter());
However, if I created an after-middleware, the after-middleware should be run after the request handled. here and when the after-middleware being execute in the laravel source code?
According to laravel official documentation,
Whether a middleware runs before or after a request depends on the middleware itself.
So, basically it depends on the handle function of the middleware. Normally we execute middleware just before handling the request like this:
public function handle($request, Closure $next)
{
// Perform some operation for Ex. checking user role
return $next($request);
}
In above function, we execute some operation before sending request to perform operation.
In the another case, middleware would perform its task after the request is handled by the application like this:
public function handle($request, Closure $next)
{
$response = $next($request);
// Perform some operation after the request is handled by the application
return $response; /* finally, response is returned */
}
In summary, In before middleware, we perform some operations at first and then send request to the application to get response which is returned to client end. In after middlware, we send request to the application at first to get response then we perform our actions and finally we return the response from middleware to client end.
You can see official docs: https://laravel.com/docs/5.8/middleware#defining-middleware

Laravel 5.0 custom 404 does not use middleware

I'm using a middleware to parse the output of the templates. This is working fine for all pages.
However when I want to show a 404 (got a custom page for that) it doesn't treat it as a http request (that's what I think) since it doesn't go through the middleware.
My question is, how to have ALL requests go through the middleware.
The error pages don't go through the routes.php.
In Kernel.php move your middleware from the $routeMiddleware array to $middleware array.
Middleware in this array will run on every request (tested in 5.1).
For people like me who spending hours in 2020 because of this weird behaviour...
Now in Laravel there is a new instrument «Fallback Routes».
Add this to /routes/web.php:
Route::fallback(function () {
return view("404"); // template should exists
});
After that all requests will go throught middlewares.
At Laravel 5.4 and probably some older ones you can modify the file app/exceptions/Handler.php and the function render like this:
if( is_a( $exception, \Symfony\Component\HttpKernel\Exception\NotFoundHttpException::class ) ) {
return redirect()->route( 'error_404' );
}
// ... old code in the function ...
in this way every 404 errors are redirected to certain real route that acts like other routes of site.
You may also submit any data from current request to show a reasonable error at the target.
I had a use case where api routes needs to always return json response.
All routes return json response (laravel checks through $request->expectsJson()) IF user specifically ask for it by sending accept: application/json header.
But many a times user doesn't send the header and they get an html response instead.
As for my use case, all api routes will always send json response we can force the routes to return json, by manually attaching accept: application/json header using a middleware.
App\Http\Middleware\ForceJsonOnAPIs.php
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Str;
class ForceJsonOnAPIs
{
/**
* Handle an incoming request.
*
* #param Request $request
* #param Closure $next
* #return mixed
*/
public function handle(Request $request, Closure $next)
{
// Force Json accept type on api routes
if ($request->is('api/*') && !Str::contains($request->header('accept'), ['/json', '+json'])) {
$request->headers->set('accept', 'application/json,' . $request->header('accept'));
}
return $next($request);
}
}
Register the middleware in App\Http\Kernel.php
// add the new middleware ForceJsonOnAPIs
protected $middleware = [
\App\Http\Middleware\ForceJsonOnAPIs::class,
// rest of the middleware,
];
Important: You can assign the middle to $middlewareGroups in Kernel like web or api, But you will get into trouble when 404 exception occurs. The issue is The error pages don't go through the routes.php (Thanks to #Björn Answer above) thus the routes middleware won't get called and the 404 will return html response.
It's the same case for validation or authentication exceptions.
In my opinion, it's best to assign the middleware in the $middleware array as it runs on each request. This way all exceptions will automatically return correct exceptions as json in all routes.

Resources