Laravel send password reset link to a separate authentication guard - php

In Laravel 5.4, is there a way to send the password reset link to a separate authentication guard instead of the default one. I am using the default PasswordResetController which does the job in this way
public function company(Request $request)
{
$this->validate(request(), [
'email' => 'required|email',
]);
$response = Password::sendResetLink([
'email' => $request->email
]);
//overridden if condition
if($response == "passwords.sent")
{
return back()->with('message','Password reset link has been sent, please check your email');
}
return back()->with('message', 'No such email address in our records, try again');
}
The sendResetLink() method checks and sends the reset link to the default guard, but I am defining a new guard in auth.php called web
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'companies',
],
sendResetLink method is like this
public function sendResetLink(array $credentials)
{
// First we will check to see if we found a user at the given credentials and
// if we did not we will redirect back to this current URI with a piece of
// "flash" data in the session to indicate to the developers the errors.
$user = $this->getUser($credentials);
if (is_null($user)) {
return static::INVALID_USER;
}
Any way for this method to check in a separate table or use a separate auth guard?

Here is my method of how to send password reset link to a guard which is a part of multi-auth system.
I am going to assume you have properly set your new guard in config/auth.php
which may look like below code:
I use the word admin for the new guard's name for better understanding.
'guards' => [
'admin' => [
'driver' => 'session',
'provider' => 'admins',
],
]
'providers' => [
'admins' => [
'driver' => 'eloquent',
'model' => App\Admin::class,
],
]
'passwords' => [
'admins' => [
'provider' => 'admins',
'table' => 'password_resets',
'expire' => 15,
],
]
You will have to create new controllers(AdminForgotPasswordController and AdminResetPasswordController) for your new guard
They both use the Password facade and AdminResetPasswordController uses Auth facade as well.
Modify the construct function according to your new guard.
So add this to both controllers, because we have specific type of guest users.
public function __construct()
{
$this->middleware('guest:admin');
}
Now we need to tell AdminResetPasswordController to use the proper guard for authenticating.
So add this method to the controller
protected function guard()
{
return Auth::guard('admin');
}
Now add this piece of code to both controllers.
protected function broker()
{
return Password::broker('admins'); //set password broker name according to guard which you have set in config/auth.php
}
Notice: It's not the only step for implementing password reset for guard you will have to take other steps like creating new routes, notifications, forms and corresponding views.

In your auth.php configuration file, you may configure multiple "guards", which may be used to define authentication behavior for multiple user tables. You can customize the included ResetPasswordController to use the guard of your choice by overriding the guard method on the controller. This method should return a guard instance:
protected function guard()
{
return Auth::guard('guard-name');
}
The magic lies, in using the broker - the PasswordBroker for your custom guard. But, you make sure to set up multiple password brokers in your auth.php configuration file.
protected function broker()
{
return Password::broker('name');
}

One simple way could be
$response = Password::broker('guard-name')->sendResetLink([
'email' => $request->email
]);
also don't miss reset function
$response = Password::broker('guard-name')->reset([
'email' => $request->email
],function(){// save changes})

Related

Is there any security issue to use this method of authenticating users in Laravel using the same login page?

I'm a new Laravel Programmer and I tried to develop an web interface which uses the same login controller for two users which are from different tables. One user is Student and the other is Instructor. They are all routed to their intended page after login but I was wondering if there is any security issue to use this concept before I reach far? Or if there is any downside of using this logic or difficulties in implementing the logic or on trying securing the routes using middlewares?
Login Controller
public function store(LoginRequest $request)
{
$credentials = $request->only('username', 'password');
if(Auth::guard("instructor")->attempt($credentials))
{
return redirect()->route("instructor");
}
else if(Auth::guard("student")->attempt($credentials))
{
return redirect()->route("dashboard");
}
else
{
return back()->with('status', 'The provided credentials do not match our records.');
}
}
Middlewares
Route::middleware("auth:instructor")->group(function (){
Route::get("/instructorDashboard", [DashboardController::class, "instructor"])->name("instructor");
});
Route::middleware("auth:student")->group(function () {
Route::get("/dashboard", [DashboardController::class, "student"])->name("dashboard");
});
Dashboard Controller (They just return normal text, not view)
class DashboardController extends Controller
{
public function student (Request $request)
{
return "Your a Student";
}
public function instructor(Request $request)
{
return "Your a Instructor";
}
}
auth.php file
'defaults' => [
'guard' => 'student',
'passwords' => 'users',
],
'guards' => [
'student' => [
'driver' => 'session',
'provider' => 'students',
],
'instructor' => [
'driver' => 'session',
'provider' => 'instructors',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
'hash' => false,
],
],
'providers' => [
'students' => [
'driver' => 'eloquent',
'model' => App\Models\Student::class,
],
'instructors' => [
'driver' => 'eloquent',
'model' => App\Models\Instructor::class
],
'users' => [
'driver' => 'eloquent',
'model' => App\Models\Student::class,
]
]
Laravel authentication is really well thought and implemented system. It has been battle tested and proven to work in production environment. What you are doing does not causes any security vunurabilities. But it might constraint on some business logic and it's understandable if you have such requirements. So your answer to questions is
Is any security issue to use this concept?
No there is no issue from any security side. Keep in mind that security is not only in one place. You might have really secured authentication system but you need to make sure other parts if application are well implemented too.
Is there any downside of using this logic or difficulties in implementing the logic or on trying securing the routes using middlewares?
There is no issue with securing routes with middleware. There are some potential downsides but software is all about compromisation. There is no downside of implementing this logic as long as you have solid robust archietecture and documentation for code flow/implementation.
If you are trying to find out vunurabilities in system. They will usually be
in custom authorization of user than that of laravel has provided. If you are really afraid about security reach out to github and look for any security bugs also read about OWASP those are frequent bugs in applications.

how to make login with two different tables depending if the user enters an email or a nickname?

I am working with Laravel for first time, I am trying to make a login depending either the user enters an email or a nickname within the same input. Database has among others 2 tables, one Users which has an email field, and another one Profile which has an nick field and which id is a foreign key from Users, in the front ed the inputs are as they come with the email input only being changed from type email to type text so the user can enter either an email or a nickname, then I have tried different ways around, last thing I tried is including a provider in the auth.php like this:
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'profiles' => [
'driver' => 'session',
'provider' => 'profiles',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
'hash' => false,
],
],
then I have tried to override the attemptLogin within the LoginController like this:
public function attemptLogin(Request $request)
{
if (Auth::guard('web')->attempt(['email' => $request->email, 'password' => $request->password])) {
$details = Auth::guard('web')->user();
$user = $details['original'];
return $user;
} else {
return 'auth with email failed';
}
if (Auth::guard('profiles')->attempt(['nick' => $request->email, 'password' => $request->password])) {
$details = Auth::guard('profiles')->user();
$user = $details['original'];
return $user;
} else {
return 'auth with nick failed';
}
}
Any help is much appreciated as I am new to Laravel and I am kinda lost
I've never tried this before, but here are a few hints:
Take a look at your config/auth.php file, more specifically the User Providers section. There you can change the model that represents the user depending on the provider.
You may also need to update which field you use for identifying the user, for that check the trait \Illuminate\Foundation\Auth\AuthenticatesUsers, more specifically the method \Illuminate\Foundation\Auth\AuthenticatesUsers::username. Usually we can overwrite it in the controller, but you might want to change that on a middleware if possible.

Custom multi-auth guard logging & routing

I'm developing a Laravel 6 app with 4 different users(with a different table for each). For this, I am using multi auth guard and created my first guard following this tutorial online (https://pusher.com/tutorials/multiple-authentication-guards-laravel).
Here is my custom guard "student"
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
'hash' => false,
],
'student' => [
'driver' => 'session',
'provider' => 'students',
],
],
And Providers
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
'students' => [
'driver' => 'eloquent',
'model' => App\Models\Student::class,
],
],
I have a custom login controller using a custom auth guard named 'student'
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
use Auth;
use Student;
class LoginController extends Controller
{
protected $redirectTo = '/dashboard';
public function __construct()
{
$this->middleware('guest')->except('destroy');
$this->middleware('student')->except('destroy');
}
public function studentLogin(Request $request)
{
$this->validate($request, [
'email' => 'required|email',
'password' => 'required|min:5'
]);
if (Auth::guard('student')->attempt(['email' => $request->email, 'password' => $request->password], $request->get('remember'))) {
return redirect()->intended('admin/home')->with('smessage', 'Logged in successfully...!');
}
return back()->withInput($request->only('email', 'remember'));
}...
}
Now, Whenever I try to log in, It seems like I am logged in and redirected to the URL defined in the middleware/RedirectIfAuthenticated.php but this error pops up "Trying to get property of non-object" which is from the common header template where no data of logged user are available on auth()->user().
If I use 'middleware'=>'auth:student', I can access the route as student and have the logged user data via auth()->user(). same for admin using 'middleware'=>'auth' but passing both auth and auth:student as an array I can't get the logged user data via auth()->user() for both user.
Here are my routes:
Route::get('/', function () {
return view('welcome');
});
Route::get('/login', 'LoginController#create')->name('login');
Route::post('login', 'LoginController#store');
Route::get('/login/student', 'LoginController#create')->name('studentlogin');
Route::post('/login/student', 'LoginController#studentLogin');
Route::get('/logout', 'LoginController#destroy')->name('logout');
Route::group(['prefix'=>'admin', 'namespace' => 'Dashboard', 'middleware'=> 'auth' ], function () {
Route::get('home', 'DashboardController#display')->name('admin.home');
Route::get('users', 'UserlistController#display')->name('admin.userlist');
Route::post('users', 'UserlistController#store')->name('admin.create');
Route::delete('users/{id}', 'UserlistController#destroy')->name('admin.deleteuser');
});
Route::group(['prefix'=>'student', 'namespace' => 'Students', 'middleware'=> 'auth:student' ], function () {
Route::get('all', 'StudentsController#display')->name('student.all');
Route::delete('all/{id}', 'StudentsController#destroy')->name('student.delete');
Route::post('all', 'StudentsController#store')->name('student.store');
});
Couldn't find any solutions from other related topics on StackOverflow. Please do correct me if I have any mistakes somewhere or let me know if you want to inspect any other files for the code.
Thanks in advance.
You don't have any data on auth()->user() because you're querying the default guard which is web.
If you want to retrieve the logged user through student guard, you have to do auth()->guard('student')->user();
Also remember to pass everytime your guard to auth and middlewares that uses auth.
I.e.: if you need to make some routes restricted only to authenticated users you will need to do:
Route::group(['middleware' => 'auth:student'], function() {
// Your restricted routes
})
Omitting :student will use the default web guard as always
Note that if you need to restrict your routes to a group of users belonging to different guards you can pass those guards to auth:
Route::group(['middleware' => 'auth:web,student'], function() {
// Your restricted routes
}];
In this way Laravel will check for both web and student guards and will automatically set the one that belongs to the authenticated user. You will also be able to retrieve your user only by doing auth()->user() forgetting to pass the right guard

user_id conflict for multiple guards laravel

I'm new to Laravel and i created two guards "user guard"(default one) and "admin guard". And i'm saving auth sessions in database instead of file.
Now the problem is user id is causing conflict in sessions table.
For example, if i create a new user in users table and new admin account in admin table both would have same id in sessions table and since the id is not unique it's automatically logging me in to admin account even though if i just login as normal user.
I've already searched on Google but couldn't find anything useful. Only this guy has asked same question but not working answer:
Multi session tables in Laravel
Here's my code:
config/auth.php
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
'hash' => false,
],
'admin' => [
'driver' => 'session',
'provider' => 'admins',
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
'admins' => [
'driver' => 'eloquent',
'model' => App\Admin::class,
]
],
AdminLoginController.php
if (auth()->guard('admin')->attempt(['email' => $request->email, 'password' => $request->password])) {
$user = auth()->guard('admin')->user();
return redirect()->route('admin');
}
There probably shouldn't be a separate table and separate eloquent model for an admin, as an admin is a User. They are just a User with elevated permissions.
Instead of creating a separate model, guard, and provider for admins, consider attaching roles and/or permissions to your User model to track if a User is an administrator.
Here's a good package to help with that: https://github.com/spatie/laravel-permission
I'm not sure it's conflicting. I believe you just don't use the guards correctly.
You need to make sure, you never call Auth::user() if you're logging in as Admin. You should always need to use Auth::guard('admin')->user().
extend the laravel default DatabaseSessionHandler and override the addUserInformation function
<?php
namespace App\Extensions;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Contracts\Container\BindingResolutionException;
use Illuminate\Session\DatabaseSessionHandler;
class CustomDatabaseSessionHandler extends DatabaseSessionHandler
{
/**
* Add the user information to the session payload.
*
* #param array $payload
* #return $this
* #throws BindingResolutionException
*/
protected function addUserInformation(&$payload): static
{
if ($this->container->bound(Guard::class)) {
info(($this->user() ? get_class($this->user()) : null));
$payload['userable_type'] = $this->user() ? get_class($this->user()) : null;
$payload['userable_id'] = $this->userId();
}
return $this;
}
/**
* Get the currently authenticated user's ID.
*
* #return mixed
* #throws BindingResolutionException
*/
protected function user(): mixed
{
return $this->container->make(Guard::class)->user();
}
}
then register the driver in AppServiceProvider
public function boot(): void
{
Session::extend('custom-database', function ($app) {
$table = $app['config']['session.table'];
$lifetime = $app['config']['session.lifetime'];
$connection = $app['db']->connection($app['config']['session.connection']);
return new CustomDatabaseSessionHandler($connection, $table, $lifetime, $app);
});
}
change your sessions table migration
public function up()
{
Schema::create('sessions', function (Blueprint $table) {
$table->string('id')->primary();
$table->string('userable_type')->nullable();
$table->foreignId('userable_id')->nullable()->index();
$table->string('ip_address', 45)->nullable();
$table->text('user_agent')->nullable();
$table->longText('payload');
$table->integer('last_activity')->index();
});
}
then change session driver in your .env file
SESSION_DRIVER=custom-database
now the sessions stored in the database are polymorphic

How to use token authentication in laravel web page

I am trying to use JWT for laravel web page instead of session. so I made some changes.
Installed jwt-auth and configure
Then changed default guard as api in config/auth.php
'defaults' => [
'guard' => 'api',
'passwords' => 'users',
],
'guards' => [
...
'api' => [
'driver' => 'token',
'provider' => 'users',
],
],
Now I am getting error
(1/1) FatalErrorException Call to undefined method
Illuminate\Auth\TokenGuard::attempt() in AuthenticatesUsers.php (line
75)
How to fix this and start token authentication for laravel web page(blades not API).
I'm also using jwt protecting our api. You should change your config like below:
'defaults' => [
'guard' => 'api',
'passwords' => 'users',
],
'guards' => [
...
'api' => [
'driver' => 'jwt', // KEY POINT!!!
'provider' => 'users',
],
],
Make sure the jwt library installed correctly:
Tymon\JWTAuth\Providers\LaravelServiceProvider::class is added in your config/app.php.
Your user model implements JWTSubject interface if you use eloquent model in your provider.
I found the solution here : https://github.com/tymondesigns/jwt-auth/issues/860
In /routes/api.php - added a few basic authentication routes
Route::post('login', 'Auth\LoginController#login');
Route::get('/user', function (Request $request) {
$user = $request->user();
return dd($user);
})->middleware('auth:api');
In /app/http/Controller/auth/LoginController.php
and then override methods in login contoller
public function login(Request $request)
{
$credentials = $request->only(["email","password"]);
if ($token = $this->guard()->attempt($credentials)) {
return $this->sendLoginResponse($request, $token);
}
$this->incrementLoginAttempts($request);
return $this->sendFailedLoginResponse($request);
}
protected function sendLoginResponse(Request $request, $token)
{
$this->clearLoginAttempts($request);
return $this->authenticated($request, $this->guard()->user(), $token);
}
protected function authenticated(Request $request, $user, $token)
{
setcookie("jwt_token", $token);
return redirect('/');
return response()->json([
'token' => $token,
]);
}
protected function sendFailedLoginResponse(Request $request)
{
return response()->json([
'message' => "not found",
], 401);
}
Adding middleware AddToken
public function handle($request, Closure $next)
{
$token = isset($_COOKIE["jwt_token"])?$_COOKIE["jwt_token"]:"";
//$request['token'] = $token;//this is working
$request->headers->set("Authorization", "Bearer $token");//this is working
$response = $next($request);
//$response->header('header name', 'header value');
return $response;
}
Register middleware in Kernel.php
protected $middleware = [
....
\App\Http\Middleware\AddToken::class,
];
I think you can try this :
'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
],
EDIT
You can find some help from the step by step example. In this example you need to focus on how to configure and use that token base authentication.
Hope this help you well.
Please refer this link. If you are using api as default then laravel authentication will throw an error.
Laravel uses default Session based authentication out of the box with the default scaffolding users-view-controller that you already have. You have additional means of adding your own custom guard in the doc, so you can make use of the guard as needed.
Therefore as #KevinPatel suggested, revert back to the default configuration, then in your route: group the route you want to be under JWT authentication, add the JWTMiddleware, in this case you have to update the controller responsible for your authentication to use the JWTAuth instead of the default auth.
You should check this answer if you need to understand it better check this answer on Laracasts
One recommended way to incorporate the JWTAuth is to go for Dingo API (of course you are not building api, but) because Dingo already added some flesh to the authentication and other routes management - so things are pretty easy to use and configure

Categories