Is there a built-in way to remove account enumeration from registration? - laravel

I have created a new site using Jetstream and Inertia. Currently the application will return a "The email has already been taken." message if a user tries to register with an existing email. Notwithstanding timing analysis, I would like to keep the existence of user accounts private. Is there a way to keep the unique constraint on email but display the same outward behavior if someone registers with an existing email? Ideally, I would like to not create a second user, but email the existing user suggesting they reset their password or ignore the email.

I agree with Unflux about not changing this, but if you need to you could modify CreateNewUser.php located at app\Actions\Fortify\CreateNewUser.php and change the validation message or modify the process.
The create() method which is responsible for creating the new user looks like:
public function create(array $input)
{
//define custom messages
$customValidationMessages = {
'email.unique' => 'New Message',
}
Validator::make($input, [
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'], //email validation rules here
'password' => $this->passwordRules(),
], $customValidationMessages)->validate(); //add the variable containing the custom message(s) here
return User::create([
'name' => $input['name'],
'email' => $input['email'],
'password' => Hash::make($input['password']),
'api_token' => Str::random(60),
]);
}
If you need to send the user an email or customize this further I suggest you look into implementing an "After Validation Hook" which you can read about here: https://laravel.com/docs/8.x/validation#after-validation-hook

Here's what worked for me:
Create a new validation Exception in app/Exceptions/ExistingUserException.php
namespace App\Exceptions;
use Illuminate\Validation\ValidationException;
class ExistingUserException extends ValidationException
{
}
Break validation into 2 steps in app/Actions/Fortify/CreateNewUser.php, throwing the extended ValidationException if the form is otherwise good
Validator::make($input, [
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255'],
'password' => $this->passwordRules(),
'terms' => Jetstream::hasTermsAndPrivacyPolicyFeature() ? ['required', 'accepted'] : '',
])->validate();
$validator = Validator::make($input, [
'email' => ['unique:users'],
], ['email.unique'=>'']);
if ($validator->fails())
{
throw new ExistingUserException($validator);
}
Create a new middleware in app/Http/Middleware/CatchExistingUser.php
<?php
namespace App\Http\Middleware;
use App\Exceptions\ExistingUserException;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Redirect;
use Illuminate\Support\Facades\URL;
class CatchExistingUser
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle(Request $request, Closure $next, $redirectToRoute = null)
{
$response = $next($request);
if ($response->exception && $response->exception instanceof ExistingUserException)
{
return $request->expectsJson()
? abort(403, 'Your email address is not verified.')
: Redirect::guest(URL::route($redirectToRoute ?: 'verification.notice'));
}
return $response;
}
}
Inject the middleware into all fortify routes via config/fortify.php
'middleware' => [CatchExistingUser::class, 'web'],
Remove the auth middleware from the verification page by overwriting the route in routes/web.php
use Illuminate\Http\Request;
use Laravel\Fortify\Contracts\VerifyEmailViewResponse;
...
Route::get('/email/verify', function (Request $request) {
$user = $request->user();
if ($user && $user->hasVerifiedEmail())
{
return redirect()->intended(config('fortify.home'));
}
return app(VerifyEmailViewResponse::class);
})
->name('verification.notice');
The custom exception is not ideal but it seems cleaner than testing the validator stored in the ValidatorException and then removing one message if there's more than one error. I think this would be needed to allow the validation of other fields while not leaking email uniqueness.

Related

How can I disable auto login after registration in laravel 8?

In using laravel 8 with fortify so I don have
App\Http\Controllers\Auth\RegisterController
Thanks in advance
First you must create a controller preferably in app\Http\Controllers\Auth called RegisteredUserController, in this controller you must overwrite the method store of the class RegisteredUserController.
Copy the store method to your new controller and delete the line $this->guard->login($user);.
It should look like this:
<?php
namespace App\Http\Controllers\Auth;
use Illuminate\Auth\Events\Registered;
use Illuminate\Http\Request;
use Laravel\Fortify\Contracts\CreatesNewUsers;
use Laravel\Fortify\Contracts\RegisterResponse;
class RegisteredUserController
extends \Laravel\Fortify\Http\Controllers\RegisteredUserController
{
public function store(Request $request, CreatesNewUsers $creator): RegisterResponse {
event(new Registered($user = $creator->create($request->all())));
return app(RegisterResponse::class);
}
}
Finally change the default /register path that points to your new controller.
Route::post('/register', 'Auth\RegisteredUserController#store');
Fortify will automatically login an user only if you return the user from the CreateNewUser class. Instead of returning the created user, throw an exception along with a flash message. Fortify will try to redirect you to the home page and will return you back to the login page as user is not authenticated showing you the flash message. Below is a peek at the process in the file App\Actions\Fortify\CreateNewUser.
public function create(array $input)
{
Validator::make($input, [
'name' => ['required', 'string', 'max:255'],
'email' => [
'required',
'string',
'email',
'max:255',
Rule::unique(User::class),
],
'password' => $this->passwordRules(),
])->validate();
$user = User::create([
'name' => $input['name'],
'email' => $input['email'],
'password' => Hash::make($input['password'])
]);
event(new Registered($user));
flash('Registration successful! Awaiting approval from admin.')
->success()
->important();
throw new \Illuminate\Auth\AuthenticationException\AuthenticationException();
}
I think there might be other solution to hook into any of the Fortify events to do that even more gracefully.

multi authentication system in laravel

I am creating multi authentication system in laravel, I have created two sperate tables for each entity, everything works fine, but I am facing only one issue after register, users who are using web guard they can log in automatically and redirect to the user dashboard and it is perfect, but in case of other users who are using different guard, when they complete the registration process, they can not log in the system automatically.
so my question how can I enable the automatic login process for other user type once they complete the register step? below is the code that I am using in my project
Route File
Route::get('/', function () {
return view('welcome');
});
Auth::routes();
Auth::routes(['verify' => true]);
Route::get('/home', 'HomeController#index')->name('home')->middleware('verified');
Route::get('/seller/dashboard', 'SellerHomeController#index')->name('seller.dashboard');
Route::get('/seller/login','Auth\Seller\SellerLoginController#showLoginForm')->name('seller.login');
Route::post('/seller/login','Auth\Seller\SellerLoginController#login');
Route::post('/seller/logout','Auth\Seller\SellerLoginController#logout')->name('seller.logout');
Route::post('/seller/password/email','Auth\Seller\SellerForgotPasswordController#sendResetLinkEmail')->name('seller.password.email');
Route::get('/seller/password/reset', 'Auth\Seller\SellerForgotPasswordController#showLinkRequestForm')->name('seller.password.request');
Route::post('/seller/password/reset','Auth\Seller\SellerResetPasswordController#reset')->name('seller.password.update');
Route::get('/seller/password/reset/{token}', 'Auth\Seller\SellerResetPasswordController#showResetForm')->name('seller.password.reset');
Route::get('/seller/register','Auth\Seller\SellerRegisterController#showRegistrationForm')->name('seller.register');
Route::post('/seller/register','Auth\Seller\SellerRegisterController#register');
SellerLoginController
namespace App\Http\Controllers\Auth\Seller;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class SellerLoginController extends Controller
{
/*
|--------------------------------------------------------------------------
| Login Controller
|--------------------------------------------------------------------------
|
| This controller handles authenticating users for the application and
| redirecting them to your home screen. The controller uses a trait
| to conveniently provide its functionality to your applications.
|
*/
use AuthenticatesUsers;
/**
* Where to redirect users after login.
*
* #var string
*/
protected $redirectTo = '/seller/dashboard';
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('guest:seller')->except('logout');
}
public function showLoginForm()
{
return view('auth.seller.login');
}
protected function attemptLogin(Request $request)
{
return $this->guard('seller')->attempt(
$this->credentials($request), $request->filled('remember')
);
}
protected function guard()
{
return Auth::guard('seller');
}
}
SellerRegisterController
namespace App\Http\Controllers\Auth\Seller;
use App\Seller;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Auth\Events\Registered;
class SellerRegisterController extends Controller
{
/*
|--------------------------------------------------------------------------
| Register Controller
|--------------------------------------------------------------------------
|
| This controller handles the registration of new users as well as their
| validation and creation. By default this controller uses a trait to
| provide this functionality without requiring any additional code.
|
*/
use RegistersUsers;
/**
* Where to redirect users after registration.
*
* #var string
*/
protected $redirectTo = '/seller/dashboard';
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('guest:seller');
}
public function showRegistrationForm()
{
return view('auth.seller.register');
}
/**
* Get a validator for an incoming registration request.
*
* #param array $data
* #return \Illuminate\Contracts\Validation\Validator
*/
protected function validator(array $data)
{
return Validator::make($data, [
'firstname' => ['required', 'string', 'max:255'],
'lastname' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:sellers'],
'business_name' => ['required', 'string', 'max:255'],
'business_description' => ['required', 'string', 'max:255'],
'business_location' => ['required', 'string', 'max:255'],
'business_website' => ['required', 'string', 'max:255'],
'password' => ['required', 'string', 'min:6', 'confirmed'],
]);
}
/**
* Create a new user instance after a valid registration.
*
* #param array $data
* #return \App\User
*/
protected function create(array $data)
{
$seller = Seller::create([
'firstname' => $data['firstname'],
'lastname' => $data['lastname'],
'email' => $data['email'],
'business_name' => $data['business_name'],
'business_description' => $data['business_description'],
'business_location' => $data['business_location'],
'business_website' => $data['business_website'],
'business_logo' => 'test_logo.jpg',
'password' => Hash::make($data['password']),
'user_type' => $data['user_type'],
]);
return $seller;
}
}// code end here
This happens because you didn't defined the guard method on the SellerRegisterController and the default implementation retrieves the default driver (which is web).
That guard method provides the guard to the automatic login process in the register method of the RegistersUsers:
$this->guard()->login($user);
You have to override the guard method in your SellerRegisterController class to return the correct driver and allow the trait to perform the login process on the correct sellers driver:
/**
* Get the guard to be used during registration.
*
* #return \Illuminate\Contracts\Auth\StatefulGuard
*/
protected function guard()
{
return Auth::guard('sellers');
}
Laravel Auth by default take User table as the main data. In case you want to do multiple auth, first thing you need to do is create a model with Notifiable trait
Model
class Admin extends Authenticatable
{
use Notifiable;
}
After that, you need to create the guard and provider in config/auth.php
The example like this
<?php
[...]
'guards' => [
[...]
'admin' => [
'driver' => 'session',
'provider' => 'admins',
],
'writer' => [
'driver' => 'session',
'provider' => 'writers',
],
],
[...]
And
[...]
'providers' => [
[...]
'admins' => [
'driver' => 'eloquent',
'model' => App\Admin::class,
],
'writers' => [
'driver' => 'eloquent',
'model' => App\Writer::class,
],
],
[...]
And in the login controller, you will need to check which guard is trying to login by doing this line
Auth::guard('admin')->attempt(['email' => $request->email, 'password' => $request->password], $request->get('remember'))
If you need more detail about it, try to read this https://pusher.com/tutorials/multiple-authentication-guards-laravel
// Adminmiddleware
if(Auth::check() && Auth::user()->role->id == 1)
{
return $next($request);
}else {
return redirect()->route('login');
}
// Authormiddleware
if(Auth::check() && Auth::user()->role->id == 2 )
{
return $next($request);
}else {
return redirect()->route('login');
}
// Admin Route
Route::group(['as'=>'admin.','prefix'=>'admin','namespace'=>'Admin','middleware'=>['auth','admin']], function (){
Route::get('dashboard','DashboardController#index')->name('dashboard');
});
// Auhtor Route
Route::group(['as'=>'user.','prefix'=>'user','namespace'=>'Author','middleware'=>['auth','user']], function (){
Route::get('dashboard','DashboardController#index')->name('dashboard');
});
// only auht route
Route::group(['middleware'=>['auth']], function(){
Route::post('favorite/{post}/add','FavoriteController#add')->name('post.favorite');
Route::post('review/{id}/add','ReviewController#review')->name('review');
Route::get('file-download/{id}', 'PostController#downloadproject')->name('project.download');
Route::post('file-download/{id}', 'PostController#downloadproject');
});

Laravel: Too few arguments to function 0 passed and exactly 1 expected"

I know this has been asked before. ive seen the questions and answers but cant find something that I can get help from.
I am trying to pass simple data into my database. so far so good.
I believe my problem arises when I try to add the userID since it has to be pulled from Auth. so here is my controller code.
side node , userID is from a foreign table called users. and userID will be used as a foreign key. in the userstable its called "id"
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class ShopController extends Controller
{
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('auth');
}
public function index()
{
return view('/shop/addShop');
}
protected function validator(array $data)
{
return Validator::make($data, [
'userid' => ['required', 'string', 'max:255'],
'shopname' => ['required', 'string', 'max:255'],
'address' => ['required', 'string', 'max:255'],
'postal' => ['required', 'string', 'max:255'],
'city' => ['required', 'string', 'max:255'],
'phone' => ['required', 'string', 'max:255'],
]);
}
/**
* Add Users shop after a validation check.
*
* #param array $data
* #return \App\
*/
protected function create(array $data)
{
$userId = Auth::id();
return User::create([
'userid'=> $data[$userId],
'shopname' => $data['shopname'],
'address' => $data['address'],
'postal' => $data['postal'],
'city' => $data['city'],
'phone' => $data['phone'],
]);
}
}
and right here you can see my route.
<?php
Route::get('/', function () {
return view('welcome');
});
Auth::routes();
Route::get('/home', 'HomeController#index')->name('home');
Route::post('/addShop', 'ShopController#create')->name('addShop');
Route::get('/addShop', 'ShopController#index')->name('addShop');
Use Request
Your create function is expecting an array. However, Laravel is passing a POST request. Therefore, use the Request class instead. Your code should read:
namespace App\Http\Controllers;
use User;
use Illuminate\Http\Request;
protected function create(Request $data)
{
$userId = Auth::id();
return User::create([
'userid'=> $data[$userId],
'shopname' => $data['shopname'],
'address' => $data['address'],
'postal' => $data['postal'],
'city' => $data['city'],
'phone' => $data['phone'],
]);
}
I got this error on my project it was because this line of code
Auth::guard()->logoutOtherDevices(); I just comment it and my logout function worked correctly.

How to read URL parameters in laravel

I am begginer, and trying to create a referral system. following is sample url
http://dev.lea.com/register?ref=mh2HPLpVSn
I try to get referral string. how can i get separately? is there need of middleware for getting cookies? if yes then what will be code of middleware? and how can i get referral reference in RegisterController?
Here is my Register Controller.
<?php
namespace Lea\Http\Controllers\Auth;
use DB;
use Lea\User;
use Lea\Http\Controllers\Controller;
use Illuminate\Support\Facades\Validator;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Http\Request;
use Cookie;
class RegisterController extends Controller
{
/*
|--------------------------------------------------------------------------
| Register Controller
|--------------------------------------------------------------------------
|
| This controller handles the registration of new users as well as their
| validation and creation. By default this controller uses a trait to
| provide this functionality without requiring any additional code.
|
*/
use RegistersUsers;
/**
* Where to redirect users after registration.
*
* #var string
*/
protected $redirectTo = '/';
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('guest');
}
/**
* Get a validator for an incoming registration request.
*
* #param array $data
* #return \Illuminate\Contracts\Validation\Validator
*/
protected function validator(array $data)
{
return Validator::make($data, [
'fname' => 'required|string|max:255',
'lname' => 'required|string|max:255',
'referral' => 'string|max:255',
'username' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:6|confirmed',
// 'tracking_id' => 'required',
]);
}
/**
* Create a new user instance after a valid registration.
*
* #param array $data
* #return \Lea\User
*/
protected function create(array $data, Request $request)
{
// printing here referral Link
echo $request()->ref;
// $referred_by = Cookie::get();
// print_r($referred_by);
die;
return User::create([
'fname' => $data['fname'],
'lname' => $data['lname'],
'username' => $data['username'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
'affiliate_id' => str_random(10),
// 'referred_by' => $referred_by,
'identity' => $data['identity'],
'dob' => $data['dob'],
'board_id' => $data['board_id'],
'class_id' => $data['class_id'],
'subject_id' => $data['subject_id'],
'institute' => $data['institute'],
'address' => $data['address'],
'city' => $data['city'],
'std_mobile' => $data['std_mobile'],
'father_name' => $data['father_name'],
'father_mobile' => $data['father_mob'],
'ipadress' => \Request::ip(),
]);
}
}
When you have an URI such as /register?ref=mh2HPLpVSn you can retrieve ref like this:
$request()->ref
in your controller! I believe you can also use $request()->has('ref') to determine if it's present in the URI.
Hope this helps you!!
In laravel 6+ inside controller you can use like
$request->get('ResourcePath')
You can get your parameters using Request access.
Here one example for get your ref code:
public function register(Request $request)
{
$allParams = $request->all();
$refParam = $request->ref;
dd($allParams, $refParam);
...
}
public function showRegister(Request $request) {
var_dump(request->ref)
}
then you can do things like validation and so on, another possiblity is to change the template of your register form and add a hidden field with the refcode from the ref param and handle the whole request in your register method.

Laravel recaptcha validation in registrar does not work

I am newbie in Laravel.
I've used this recaptcha package: https://github.com/greggilbert/recaptcha
And the documentation saids that:
In your validation rules, add the following:
$rules = array(
// ...
'g-recaptcha-response' => 'required|recaptcha',
};
By the way I use the laravel 5's Registrar:
<?php namespace taxman\Services;
use taxman\User;
use Validator;
use Illuminate\Contracts\Auth\Registrar as RegistrarContract;
class Registrar implements RegistrarContract {
/**
* Get a validator for an incoming registration request.
*
* #param array $data
* #return \Illuminate\Contracts\Validation\Validator
*/
public function validator(array $data)
{
return Validator::make($data, [
'name' => 'required|max:255',
'email' => 'required|email|max:255|unique:users',
'password' => 'required|confirmed|min:4',
'telephone' => 'required',
'g-recaptcha-response' => 'required|recaptcha',
]);
}
/**
* Create a new user instance after a valid registration.
*
* #param array $data
* #return User
*/
public function create(array $data)
{
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
]);
}
}
But in this case the laravel return with a error message: "The g-recaptcha-response is required", however in the view I used this command:
<div class="form-group">
{!! Recaptcha::render() !!}
</div>
And yes, the field is not empty!
So, I think, I should put the 'g-recaptcha-response' => 'required|recaptcha' somewhere else?
Because it seems, in the Registrar's validator does not work.
if someone is still pulling their hair because of this issue just remove the 'recaptcha' in validation rule.
use
'g-recaptcha-response' => 'required'
instead of
'g-recaptcha-response' => 'required|recaptcha'
see http://tuts.codingo.me/google-recaptcha-in-laravel-application/
if you paste the following in the controller where this action happends it should work.
$this->validate($request,['g-recaptcha-response' => 'required|recaptcha']);
According to the documentation https://developers.google.com/recaptcha/docs/verify, you need to check g-recaptcha-response POST parameter. So pass it to the validator() function.

Resources