I created a Laravel REST API. My routes are auto-generated with Route::apiResource().
Authentication is set up using Passport and is working as intended.
Now I tried to implement an Admin role with a Middleware that I attach to store, update and delete in my Controller:
$this->middleware('admin-only', ['only' => ['store', 'update', 'delete']]);
I want my application to respond with 403 Insufficient permissions if the User is not an Admin.
For GET it is working as intended, but for PUT requests Laravel is always returning a Response with 302 Found.
AdminOnly Middleware
namespace App\Http\Middleware;
use Illuminate\Http\Response;
use Closure;
use Illuminate\Support\Facades\Auth;
class AdminOnly {
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next) {
$user = Auth::user();
if (!$user->admin) {
return Response::create('Insufficient permissions', 403);
}
return $next($request);
}
}
I found my Error by throwing an Exception in constructor of Symfony\Component\HttpFoundation\RedirectResponse class (thanks to #jedrzej.kurylo) and examining the produced stack trace in laravels errorlog (storage/logs/laravel.log).
The AdminOnly middleware was never called, because I forgot to onclude the id in the request (/person instead of /person/{id}). This caused my default route to trigger and redirect to the application base.
Related
I am trying to do a basic login session in Laravel and I want the user not to be able to go to the login page once they are logged in. I tried to redirect it to the home page but it still shows my login page if I access it.
Do I have to add anything else?
This is the middleware that I used:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class UserAuth
{
/**
* 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)
{
if($request->path()=="login" && $request->session()->has('user'))
{
return redirect('/');
}
return $next($request);
}
}
Make sure you've actually registered your middleware (whether in app/Http/Kernel or with the routes/controller. Also ensure you run the middleware after Laravel's normal HTTP middleware otherwise it may not have started the session/fetched the user yet.
I'm not sure $request->session()->has('user') is what you think it is - that's checking to see if the session has a key called user. You might want to check $request->user() instead.
I am building an application, where there would be main application users (say support users) and separate client users (the application can have many different clients and each clients can have many users), every client has its own separate database, but the codebase for the entire application would be the same for every client.
What I wanted to achieve is, before calling auth in the main application, I wanted to call a middleware, which would detect a parameter (say db_slug) from request URL and according to that param it will change the DB respectively. And then login them to the client user to their respective DB.
Note: The client users will not be a part of the main DB. Their record would be only in their Client's DB.
But I am failing to do so, as my auth middleware is called first, before my custom middleware, and on accessing auth routes, it is saying unauthenticated, since that specific client user is not a part of my main application.
Note: I am using sanctum auth.
What I tried is, created a middleware called ClientDBMiddleware
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use DB;
use Config;
class ClientDBMiddleware
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle(Request $request, Closure $next)
{
if($request->has('db_slug')){
$dbSlug = $request->db_slug;
DB::purge('mysql');
Config::set('database.connections.mysql.database', "db_$dbSlug");
}
return $next($request);
}
}
and applied it my api.php auth routes before auth middleware
Route::group(['middleware' => ['clientDB', 'auth:sanctum']], function () {
Route::get('/me', [UserController::class, 'me']);
});
Important Note :
If there is a better approach to achieve my required thing, then that would be highly appreciated, please help me with it.
I am trying to get the source of the url that does the request POST/GET to a route. For example:
Route::post('/do-something', somethingController#getWhoRequesting);
Then as example, on the view I do ajax or a simple form post to that route, which is from localhost:8888/my-web-view. How do I get the url of who is requesting to my /do-something endpoint? I expect on getWhoRequesting(), but I get the '/my-web-view'.
Thanks in advance
You can get full url from requestin controller using request
request()->fullUrl(); or $request->fullUrl();
Updates:
If you want to capture all request then you can create middleware and assign it to all routes
php artisan make:middleware RequestCapture
then file will be look like this
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class RequestCapture
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle(Request $request, Closure $next)
{
//here you can capture all $request for assigned middleware routes
return $next($request);
}
}
Also register this middleware in kernal.php in protected $routeMiddleware
'requestCapture' => \App\Http\Middleware\RequestCapture::class,
then you can assign to routegroup
Route::group(['middleware' => 'requestCapture'], function () {
});
You could use path method of Laravel. The path method returns the request's path information. So, if the incoming request is targeted at http://example.com/foo/bar, the path method will return foo/bar:
$uri = $request->path();
I hope it is something you are looking for ??
I am coding a Laravel API hosted on api.url.com for an application hosted on www.myurl.com on a server different to the Lravel API using Laravel Fortify.
The problem comes when the user verifies their email on the generated link, they are redirected not to the external application but again back to the API, but on their browser.
THe docs state that this configuration in the Authenticate.pgp Middleware would work:
<?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->expectsJson()) {
return url(env('SPA_URL') . '/dashboard');
}
}
}
Where SPA_URL is set in the .env to www.myurl.com.
This redirects to api.url.com/www.myurl.com/dashboard Obviously causing a 404 response.
I then tried to call an action on a controller as such
public function redirectDashboard(Request $request){
return redirect()->away('www.myurl.com/dashboard');
}
This again redirects to api.url.com/www.myurl.com/dashboard Obviously causing a 404 response.
I have no clue why redirect()->away would still redirect to a url on the same API domain.
Any help would be appreciated, thank you.
The RedirectTo method will redirect to request.
Try to write new middleware
public function handle($request, Closure $next)
{
//check here if the user is authenticated
if ( ! $this->auth->user() )
{
// here you should redirect to another site
return redirect(env('SPA_URL') . '/dashboard');
}
return $next($request);
}
I finally realised what was happening with the redirect: I was using the redirect() helper function rather than the Redirect::class. The helper function appends the current domain to any url or away method call whereas the Redirect::class allows you to redirect to anywhere you need.
I'm starting my first Laravel project (first MVC / OOPHP project infact) and could use some help with routes.
I followed the guide at https://medium.com/employbl/easily-build-administrator-login-into-a-laravel-5-app-8a942e4fef37 to add a check if user is admin when loading a page. It works for normal view routes, e.g.
Route::get('/admin/something', 'AdminController#admin_something')
->middleware('is_admin')
->name('admin');
But I now have a resource route and get an error when I add the two -> lines to the route. So this works with no auth:
Route::resource('thingies', 'ThingyController');
But with this:
Route::resource('thingies', 'ThingyController')
->middleware('is_admin')
->name('admin');
I get the error Symfony \ Component \ Debug \ Exception \ FatalThrowableError (E_RECOVERABLE_ERROR)
Too few arguments to function Illuminate\Routing\PendingResourceRegistration::name(), 1 passed in /var/www/routes/web.php on line 24 and exactly 2 expected
What do I need to do differently to add this auth to a resource route?
The is_admin() function from the tutorial:
const ADMIN_TYPE = 'admin';
const DEFAULT_TYPE = 'default';
public function isAdmin() {
return $this->type === self::ADMIN_TYPE;
}
And the middleware:
namespace App\Http\Middleware;
use Closure;
class IsAdmin
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if(auth()->user()->isAdmin()) {
return $next($request);
}
return redirect('home');
}
}
You can't name your route "admin" with ->name('admin'); at the end of your resource route because it concerns all CRUD routes in one statement and Laravel build-in system has already named them.
You're on the good way, just delete the last line like so, it should works :
Route::resource('thingies', 'ThingyController')
->middleware('is_admin');
You cannot give a 'name' to a resource route. but you can give names to each method in the resource controller separately.
to do so name() function required 2 parameters.
method name
name for that method route.
,
Route::resource('thingies', 'ThingyController')
->middleware('is_admin')
->name('create', 'admin.create');