Laravel 6 - How to restrict route by user field value? - laravel

Atm, I use a steamauth API to grab a users steamid and pass it into user->steamid, but I want to restrict it to, if the field named steamid in users is not null(has already a steamid) they cant enter the route and will get a redirect back. I have tried for several hours now, but i cant seem to get it to working. This is my AuthController atm:
use Invisnik\LaravelSteamAuth\SteamAuth;
use App\User;
use Illuminate\Support\Facades\Auth;
use Illuminate\Http\Request;
class AuthController extends Controller
{
/**
* The SteamAuth instance.
*
* #var SteamAuth
*/
protected $steam;
/**
* The redirect URL.
*
* #var string
*/
protected $redirectURL = '/';
/**
* AuthController constructor.
*
* #param SteamAuth $steam
*/
public function __construct(SteamAuth $steam)
{
$this->steam = $steam;
$this->middleware('auth');
}
/**
* Redirect the user to the authentication page
*
* #return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function redirectToSteam()
{
return $this->steam->redirect();
}
/**
* Get user info and log in
*
* #return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function handle()
{
if ($this->steam->validate()) {
$info = $this->steam->getSteamId();
if (!is_null($info)) {
Auth::user()->update(['steamid' => $info]);
return redirect($this->redirectURL); // redirect to site
}
}
return $this->redirectToSteam();
}

Added this custom middleware and it works:
public function handle($request, \Closure $next)
{
/*$user = User::where('steamid', $request)->first();
if (!is_null($user)) {
return redirect('/profile');
}*/
if ($request->user()->steamid !== null){
return redirect('/profile')->with('denied', 'Du kan kun tilføje én steamprofil');
}
return $next($request);
}

Related

Can I use dynamic model on middleware?

I have 2 routes that requires a person to be tagged to access the discussions.
http://localhost:8000/api/fieldReports/{fieldReport}/discussions
http://localhost:8000/api/agendas/{agenda}/discussions
Currently, I have created this middleware, but instead of pointing right to a specific model, and duplicate it for each model with the exact same functionality, I want it to be more reusable.
Middleware\ForbidUntaggedUser.php
class ForbidUntaggedUser
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle(Request $request, Closure $next)
{
$user = $request->user();
$report = $request->report; // <-- I hardcoded the model, I want this to be dynamic
// The `taggedUsers` remains the same (identical) for each model that has tagging system on it.
if (!$report || !$report->taggedUsers->contains($user->id)) {
return response()->json('Your action is unauthorized.', 403);
}
return $next($request);
}
}
I've tried to use Policy but it doesn't work, so I think I need a middleware for this.
Policies\FieldReportDiscussionPolicy.php
class FieldReportDiscussionPolicy
{
use HandlesAuthorization;
/**
* Determine whether the user can view any models.
*
* #param \App\Models\User $user
* #return mixed
*/
public function viewAny(User $user, FieldReport $fieldReport)
{
return $user->can('view any fieldReportDiscussion')
&& $fieldReport->taggedUsers->contains($user->id);
}
... // and so on..
}
Controllers\FieldReportDiscussionController.php
class FieldReportDiscussionController extends Controller
{
protected $model;
/**
* Create new instance.
*
* #return void
*/
public function __construct()
{
$this->authorizeResource(
FieldReportDiscussion::class,
['fieldReportDiscussion', 'fieldReport'] // This gave me error "Array to string conversion"
);
$this->model = new FieldReportDiscussion;
}
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index(FieldReport $fieldReport)
{
$discussions = $this->model->registries($fieldReport)->paginate(100);
return response()->json($discussions);
}
}
I need the dependency injection on the controller because the route is nested with each model as the parent, like this one..
routes\api.php
Route::apiResource('fieldReports', FieldReportController::class);
Route::apiResource('fieldReports.discussions', FieldReportDiscussionController::class)->except(['update'])->parameter('discussions', 'fieldReportDiscussion');
So, what's the solution for this? Can I make it dynamic (the first request object)?
I think you're on the right track with using middleware, although you'll need some conditional checks, something along the lines like:
class ForbidUntaggedUser
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle(Request $request, Closure $next)
{
$instance = null;
if ($request->report !== null) {
$instance = $request->report;
} else if ($request->agenda !== null) {
$instance = $request->agenda;
}
if (!$instance || !$instance->taggedUsers->contains(auth()->id())) {
return response()->json('Your action is unauthorized.', 403);
}
return $next($request);
}
}
If you have named correctly your params in your controller i.e. in a resoruce controller
<?php
namespace App\Http\Controllers;
use App\Models\Agenda;
class AgendaController extends Controller
{
public function show(Agenda $agenda)
{
....
}
}
The first parameter of your request will be the named model, so you can get the model with the getModel() function, if you are not sure, you can search it with findModel() function.
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\Request;
class MyMiddleware
{
/**
* 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)
{
$model = $this->getModel($request);
$tryGetModel = $this->findModel($request);
}
private function getModel(Request $request) : Model|null
{
$modelParameterName = $request->route()->parameterNames()[0];
return $request->$modelParameterName ?? null;
}
private function findModel(Request $request) : Model|null
{
foreach($request->route()->parameters() as $param)
{
if($param instanceof Model)
return $param;
}
return null;
}
}

Laravel passport / allow api routes for guests / bypass Laravel api middleware

This is the only one solution for passport authentication that I have found for a week of struggles. Enjoy!
Paste code bellow in file app/Http/Middleware/Authenticate.php
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Auth\Middleware\Authenticate as Middleware;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Contracts\Auth\Factory as Auth;
class Authenticate extends Middleware
{
/**
* The authentication factory instance.
*
* #var \Illuminate\Contracts\Auth\Factory
*/
protected $auth;
/**
* Create a new middleware instance.
*
* #param \Illuminate\Contracts\Auth\Factory $auth
* #return void
*/
public function __construct(Auth $auth)
{
$this->auth = $auth;
}
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #param string[] ...$guards
* #return mixed
*
* #throws \Illuminate\Auth\AuthenticationException
*/
public function handle($request, Closure $next, ...$guards)
{
$this->authenticate($guards);
return $next($request);
}
/**
* Determine if the user is logged in to any of the given guards.
*
* #param array $guards
* #return void
*
* #throws \Illuminate\Auth\AuthenticationException
*/
protected function authenticate(array $guards)
{
if (empty($guards)) {
return $this->auth->authenticate();
}
foreach ($guards as $guard) {
if ($this->auth->guard($guard)->check()) {
return $this->auth->shouldUse($guard);
}else{
return null;
}
}
throw new AuthenticationException('Unauthenticated.', $guards);
}
}
After this, all routes will be available for guests.
Create new middleware DenyIfNotAuthenticated. Add there code from default app/Http/Middleware/Authenticate.php. It will be like this:
<?php
namespace App\Http\Middleware;
use Illuminate\Auth\Middleware\Authenticate as Middleware;
class DenyIfNotAuthenticated 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');
}
}
}
Add to Kernel.php file line: 'auth.deny' => \App\Http\Middleware\DenyIfNotAuthenticated::class, bellow 'auth' => \App\Http\Middleware\Authenticate::class,
Routs in routes/api.php looks like this:
Route::apiResource('recipes', 'RecipesController'); // accessible for guests
Route::group(['middleware' => ['auth.deny:api']], function () {
Route::get('ingredients', 'IngredientsController#index');
}); // accessible only for authorized users

View [auth.login] not found

I am working on login part of application where i am creating two seperate login for admin and user.
My Controller structure is like :
Controller - Admin (For Admin)
LoginController.php
-- Auth
login.blade.php
.... Auth (For Normal user)
LoginController.php
...
Views:
Admin
login.blade.php
auth
login.blade.php
till now i'm working on the admin part.
LoginController.php
<?php
namespace App\Http\Controllers\Admin;
use Auth;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
class LoginController extends Controller
{
use AuthenticatesUsers;
/**
* Where to redirect admins after login.
*
* #var string
*/
protected $redirectTo = '/admin';
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('guest:admin')->except('logout');
}
/**
* #return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function showLoginForm()
{
return view('admin.auth.login');
}
public function login()
{
$this->validate($request, [
'email' => 'required|email',
'password' => 'required|min:6'
]);
if (Auth::guard('admin')->attempt([
'email' => $request->email,
'password' => $request->password
], $request->get('remember'))) {
return redirect()->intended(route('admin.dashboard'));
}
return back()->withInput($request->only('email', 'remember'));
}
/**
* #param Request $request
* #return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
/*public function logout(Request $request)
{
Auth::guard('admin')->logout();
$request->session()->invalidate();
return redirect()->route('admin.login');
} */
}
RedirectIfAuthenticated.php
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
class RedirectIfAuthenticated
{
/**
* 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)
{
switch($guard){
case 'admin':
if (Auth::guard($guard)->check()) {
return redirect('/admin');
}
break;
default:
if (Auth::guard($guard)->check()) {
return redirect('/');
}
break;
}
return $next($request);
}
}
Authenticate.php
<?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
*/
protected function redirectTo($request)
{
if (! $request->expectsJson()) {
return route('login');
}
}
}
web.php
Route::group(['prefix' => 'admin'], function () {
Route::get('login', 'Admin\LoginController#showLoginForm')->name('admin.login');
Route::post('login', 'Admin\LoginController#login')->name('admin.login.post');
Route::get('logout', 'Admin\LoginController#logout')->name('admin.logout');
//Route::get('dashboard', 'Admin\LoginController#dashboard')->name('admin.dashboard');
Route::group(['middleware' => ['auth:admin']], function () {
Route::get('/dashboard', function () {
return view('admin.dashboard.index');
})->name('admin.dashboard');
});
whenever i access dashboard throgh url i get View[auth.login] not found.
You can modify the file Authenticate.php to receive the guard name in redirectTo method.
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Auth\Middleware\Authenticate as Middleware;
class Authenticate extends Middleware
{
/**
* #var array
*/
protected $guards = [];
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #param string[] ...$guards
* #return mixed
*
* #throws \Illuminate\Auth\AuthenticationException
*/
public function handle($request, Closure $next, ...$guards)
{
$this->guards = $guards;
return parent::handle($request, $next, ...$guards);
}
/**
* 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 (!$request->expectsJson()) {
if (reset($this->guards) === 'admin') {
return route('admin.login');
}
return route('login');
}
}
}
Note that it may be necessary to execute php artisan cache:clear after the change.
source

laravel 5.7 multi auth email verification

I'm new to Laravel and I'm trying to set up an email verification for job_seeker but after I register a new job_seeker I redirect to profile page which must be protected with job_seeker_verified middleware
in normal case I must be redirecting to job_seeker/verify which uses the route named job_seeker_verification.notice with the controller verification_controller and the function that shows the view with verify message but instead I get
forbidden page 403
namespace App\Http\Controllers\job_seeker;
use App\Job_seeker;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class job_seeker_profile_controller extends Controller
{
public function __construct()
{
$this->middleware(['job_seeker_auth', 'job_seeker_verified']);
}
public function show_profile(Job_seeker $job_seeker)
{
return view('profile.job_seeker_profile');
}
}
namespace App\Http\Middleware;
use Illuminate\Support\Facades\Redirect;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Support\Facades\Auth;
use Closure;
class Ensure_Job_Seeker_Is_Verified
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next, $guard = null)
{
$guard == 'job_seeker';
if (
!Auth::guard($guard)->user() || (Auth::guard($guard)->user() instanceof MustVerifyEmail &&
!Auth::guard($guard)->user()->hasVerifiedEmail())
) {
return $request->expectsJson()
? abort(403, 'Your email address is not verified.')
: Redirect::route('job_seeker_verification.notice');
}
return $next($request);
}
}
namespace App\Http\Controllers\job_seeker;
use Illuminate\Http\Request;
use App\Job_seeker;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\VerifiesEmails;
class Verification_Controller extends Controller
{
use VerifiesEmails;
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
protected $redirectTo = 'job_seeker.profile';
public function __construct()
{
$this->middleware('job_seeker_auth');
$this->middleware('signed');
$this->middleware('throttle:6,1')->only('resend');
}
/**
* Display the specified resource.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function show(Request $request)
{
return $request->user()->hasVerifiedEmail()
? redirect($this->redirectPath())
: view('profile.job_seeker_verify');
}
public function verify(Request $request)
{
if ($request->route('id') != $request->user()->getKey()) {
throw new AuthorizationException;
}
if ($request->user()->hasVerifiedEmail()) {
return redirect($this->redirectPath());
}
if ($request->user()->markEmailAsVerified()) {
event(new Verified($request->user()));
}
return redirect($this->redirectPath())->with('job_seeker_verified', true);
}
}
Route::get('job_seeker_email.resend', [
'as'=>'job_seeker_email.verification.resend', 'uses'=>'job_seeker\Job_Seeker_Verication_email#resend'
]);
Route::get('job_seeker/verify', [
'as'=>'job_seeker_verification.notice', 'uses'=>'job_seeker\Verification_Controller#show'
]);
Route::get('job_seeker/verify/{id}', [
'as'=>'job_seeker_verification.verify','uses'=>'job_seeker\Verification_Controller#verify'
]);
Remove
$this->middleware('job_seeker_auth');
From the verification_controller constructor because it's returning 403 before it reaches the show or verify method
An unverified user can't verify themselves if they need to be verified to do so

laravel redirect to url after login

I have trouble with redirecting to an url after login.
The situation is that someone visits a blog post, and needs to login before adding a comment. So the user clicks on the login link and logs in on "auth/login", and is always redirected to "/home".
I want the user to be redirected to the blogpost when an url is set like "auth/login?redirect=url/to/blogpost"
I have the following Middleware:
app\Http\Middleware\RedirectIfAuthenticated
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Contracts\Auth\Guard;
class RedirectIfAuthenticated
{
/**
* 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->check()) {
return redirect('/home');
}
return $next($request);
}
}
app\Http\Middleware\Authenticate
<?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 $next($request);
}
}
Why don't you use the intended method on redirector? Read about this in docs
The intended method on the redirector will redirect the user to the URL they were attempting to access before being caught by the authentication filter. A fallback URI may be given to this method in case the intended destination is not available.
I've decided to copy and paste the getLogin function of the trait AuthenticatesUsers into my AuthController. I overwrite the function AND keep the trait as is.
I've just added
\Session::put('url.intended',\URL::previous());
If you're using standard authentication from Laravel 5, find a app/Http/Controllers/Auth/AuthController.php file and change $redirectPath to this:
protected $redirectPath = '/url/to/blogpost';

Resources