How to Send User back to intended page in Laravel - laravel

I am building a Laravel App where people can read Books.
I have middleware, that control access to the Book
public function handle($request, Closure $next)
{
if (!(auth()->user()->isBasic() || auth()->user()->isCouple() || auth()->user()->isFamily())) {
return redirect(route('subscription-plan'));
}
return $next($request);
}
Inweb.php
To see the book and the details of the book
Route::get('/books/{book}', [BooksController::class, 'show'])->name('book')->middleware('verified');;
To read the book
Route::middleware(['basic'])->group(function (){
Route::get('/read/{book_id}', [BooksController::class, 'read'])->name('read');
});
I have 3 roles
Basic
Couple
Family
Subscriber
If the logged-in user is a subscriber, it redirects to /subscription-plan when he tried to access Read.
After subscription, I want to be able to redirect after subscription to the book I was trying to read before I was redirected to /subscription-plan.
I was researching how to use intended
redirect($this->redirectPath());
redirect()->intended($this->redirectPath());
But none of them is working.
Here is my final code
public function handleGatewayCallback()
{
...
redirect()->intended();
}

Not sure what are you doing on the subscription page.
Just thinking that the user will leave the app complete the payment externally "paypal" and the payment processor will redirect the user back to your system on your fallback url , on success or not.
If this is your case I don't think intended will work for you..
My suggestion is to use session and store the URL where you want to return the user after he has completed the payment on the subscription page.
Exmaple:
Store session key:
$request->session()->put('url_on_success', '/url-on-success');
Get the stored session:
$url = $request->session()->get('url_on_success');
Check : https://laravel.com/docs/6.x/session for more how to use sessions on Laravel.

Related

How to authenticate a user by its user id in Laravel in a custom middleware

How would you guys go about implementing something like this? I use Laravel as an API only. We have other framework where the login was implemented where it saves an httpOnly cookie (sessionId) after the user logs in. That's the main framework. We're migrating away from that old framework (Zend).
With the sessionId sent to Laravel from, say, a JS frontend, I'm able to lookup the current user based on the sessionId. That sessionId is then used to query the session database. I've created a middleware called "CheckForCurrentUser.php":
[..]
public function handle(Request $request, Closure $next)
{
// The reason for this is that the OPTIONS (request) does not include the cookie in the request.
$method = $request->method();
// SESSIONID is the name of the cookie created from the main framework
// once a user is logged in.
// SESSIONID is an exception in EncryptCookies.php
$sessionId = request()->cookie('SESSIONID');
if ($method === 'POST' && $sessionId !== '') {
// This function is only to get the logged in user id from the session database
$userId = $this->notImportantFunction($sessionId);
if ($userId) {
// User id found so make current user for this Laravel API
Auth::loginUsingId($userId);
} else {
// Instructs the frontend to let user log back in.
return abort(401);
}
}
return $next($request);
}
To recap, a user cannot authenticate from this Laravel application nor can I send an authentication token. They logged in from another framework. Laravel has access to the main framework databases.
This setup works. Using use Illuminate\Support\Facades\Auth; I use that as the "current user" in any controller/model.
Kernel.php looks something like:
[..]
protected $middleware = [
[..]
\App\Http\Middleware\CheckCurrentUser::class,
[..]
];
Before I go any further, is that how you'd implement something like this? It does work (ish) but I do not get the SESSIONID unless I check for a POST request.
I do not like this setup. I'm now using lighthouse and having issues using both the #auth and #inject directives. This is due to how I authenticate a use with Laravel so sorting how I authenticate a user should sort Lighthouse. Any tips on how to refactor this the right way? My routes are with /api/some-string
Lighthouse is not the issue. Should I send a authentication header, Lighthouse works. I believe Laravel does something behind the scenes with it sees a token in the header. I cannot send an authentication. I can only rely on the cookie. It's httpOnly so I have no access to that from JavaScript.

Laravel passport - Allow user to act as/login as other user

I'm working on an application where some users should have access to other user accounts. For example: In a family, the mother and all 3 kids have an account. Now the mother should have access to all of the kids accounts.
Is there a possibility to setup something like this in Laravel using Passport? I thought about a "permission" database table with two columns (parent_account, child_account). Parent accounts could then switch between accounts where they have the permission.
Perfect would be something like a middleware where I can set Auth::actAs($child);and after that every Auth::user() call would be the child until I switch back to the "normal" account.
Additional information: I'm using Laravel to provide an API for my React Frontend Application. I tried the Auth::loginUsingId function, but when I use it I get logged out and I get the Method Illuminate\Auth\RequestGuard::loginUsingId does not exist. Exception.
I am using Laravel Version 6.9.0
I found a solution to my problem.
I added a middleware that contains this piece of code:
public function handle($request, Closure $next)
{
$activeChild = Auth::user()->activeChild; // id of child user
if ($activeChild) {
Auth::setUser($activeChild);
}
return $next($request);
}
After that I added this middleware to all routes:
Route::group(['middleware' => ['actAsUser']], function () {
// some routes
});

How to hide login form after reaching the total of failed login attempts?

I want to hide the login form and display an error message instead, but I can't.
I tried to put the code below that rewrites the action on the controller that shows the form, but the method that checks for too many login attempts doesn't seem to work and never returns true.
public function showLoginForm(Request $request)
{
if (method_exists($this, 'hasTooManyLoginAttempts') &&
$this->hasTooManyLoginAttempts($request) ) {
$seconds = $this->limiter()->availableIn($this->throttleKey($request));
return view('auth.block', array(
'seconds' => $seconds
));
}
return view('auth.login');
}
I managed the authentication process with php artisan make: auth login controller is the default generated by Laravel, the only change is in the action that displays the form.
The function hasTooManyLoginAttempts() needs, in the $request, the username (usually the email) as a key to know if the user has reached his max login attempts.
If, in the $request, there is not the username with a value the function is unable to verify the user login attempts.
So you cannot really know who is the user that wants to get your login form, you know who is only after he submitted the form.
IMHO the only way could be to add a username parameter to the GET request but you shoud provide it with some workarounds: cookies, session etc.
Looking at Laravel's code, it checks for hasTooManyLoginAttempts based on throttleKey and maxAttempts.
The throttleKey is dependent on the user's email and IP address. So the output of the following code is something like: info#example.com|127.0.0.1 and that is your throttleKey.
protected function throttleKey(Request $request)
{
return Str::lower($request->input($this->username())).'|'.$request->ip();
}
Now Laravel gets the user's email (username) from $request->input($this->username()) when you send a POST request, which you don't have access to in the showLoginForm method because it's called on the GET request.
Anyway, if you want to block the login form you'll need to come up with your own unique throttleKey and then override the method. Say you want your throttleKey to be based only on the IP address - which is not recommended. Here's how you do it:
// In LoginController.php
protected function throttleKey(Request $request)
{
return $request->ip();
}

Laravel Auth::id() return null after login

I have a login form to access to my web page.
In my local computer everything works fine. But now I upload my project to my server and when I login the directive #auth() is null.
I put in my controller this: dd(Auth::id()); and in my local server returns a Id but in the production server returns null...
in web.php I have tis code:
Route::group(['middleware' => 'role:admin' OR 'role:user'], function () {
Route::get('/users/inicio', function(){
dd(Auth::id());
return view('frontend.dashboardUser');});
});
This return null
Can you help me?
Thank you
I think there might be some session problem, It might not be maintaining the session state.
My suggestion:
Try echo session_id() multiple times, If every time different id is generated then there will be some problem with the session on server otherwise not.
Have you registered a new user after you pushed your code to the production? I mean have you logged in using an existing user on production? I believe your production and local Database is different and the user who exists on local does not exist on production DB.
Register a new user and login as the new user and then try accessing the route to see if you get the auth id.
For a security reason, you can't access the login user or any other session into the web.php file as well as a constructor of the class.
To archive this you can use middleware something like this:
public function __construct() {
$this->middleware(function (Request $request, $next) {
if (!\Auth::check()) {
return redirect('/login');
}
$this->userId = \Auth::id(); // you can access user id here
return $next($request);
});
}
This link can help you more. Good luck!!!

Is it possible to force logout using user id in Laravel?

I'm wondering if there is any simple way to force logout different users by their id? For example I need to block currently lodged in user so I want to log out him after I set his status to block.
P.S.
I cant use middleware for this to check on each request.
I do this inside the Authenticate middleware
if (!Auth::user()->isActive()) {
Auth::logout();
return Redirect::home();
}
The user is already loaded there, no additional database query is needed here.
I don't think that's a performance issue, you just do a little if statement and you do it only if the user needs to be authenticated.
It's super easy if you are using database session driver:
DB::table('sessions')->where('user_id', $userId)->delete();
#PerterPan666 thanks, ya i ended up creating a middleware and adding it to the web group.
public function handle($request, Closure $next)
{
if (Auth::check())
{
if (Auth::User()->is_active != 'Y')
{
Auth::logout();
return redirect()->to('/')->with('warning', 'Your session has expired because your account is deactivated.');
}
}
return $next($request);
}
For anyone using later Laravel 5.6+, there's a method available for this built in. Doesn't mention where to call logoutOtherDevices, but LoginController#authenticated looks to work well as you can pass through their password, as required by the method
https://laravel.com/docs/5.8/authentication#invalidating-sessions-on-other-devices
public function authenticated(Request $request, $throttles)
{
\Illuminate\Support\Facades\Auth::logoutOtherDevices($request->get('password'));

Resources