Laravel email verification - Forced to be logged in - php

After following the installation for enabling the new built-in email verification, all is working good (sending email after registration and clicking the activation enable the account).
But, I'm faced with the case where the user must be logged-in in order for the verification process to engage. Meaning, if the user is not logged in prior to use the verification link, he will be redirected to the login page and then be presented the /resources/view/auth/verify.blade.php page.
I am reaching out to the community to see if this it intentional, a bug or something I'm doing wrong? Did someone run in the same situation?
The site I'm setting up have public access to most pages, but restricted to some (for now the user portal). I set up the routes/web.php as follow:
// Authentication
Auth::routes(['verify' => true]);
Route::group(['middleware' => ['auth', 'verified'], 'as' => 'portal.', 'prefix' => '/portal'], function () {
Route::get('/', 'PortalController#index');
Route::get('/profile', 'PortalController#index')->name('profile');
Route::get('/orders', 'PortalController#index')->name('orders');
});
By tracing the verification process, I was able to find out the process is forcing a log-in in the VerificationController constructor via the middleware shown below.
public function __construct()
{
$this->middleware('auth');
$this->middleware('signed')->only('verify');
$this->middleware('throttle:6,1')->only('verify', 'resend');
}
By commenting the first line or adding ->except('verify'), the log-in page is not shown but an error is thrown at the Traits VerifiesEmails method Verify like below, since the user is obviously not logged it (the $request->user() is null).
public function verify(Request $request)
{
if ($request->route('id') == $request->user()->getKey() &&
$request->user()->markEmailAsVerified()) {
event(new Verified($request->user()));
}
return redirect($this->redirectPath())->with('verified', true);
}
My question, is there a way to get it to work without being loged-in beforehand, or is this the way the Verification process is implemented in 5.7? ... or what am I doing wrong?

is there a way to get it to work without being loged-in beforehand, or
is this the way the Verification process is implemented in 5.7? ... or
what am I doing wrong?
This is the way the Verification process is implemented in Laravel 5.7. Laravel uses signed URLs for verification. The URL is generated with an id parameter (id as user ID) and when the user clicks on the verification link, 3 checks are done:
Is the signature valid? (signed middleware)
What's the user ID in the signature? That's the ID that would ultimately be validated
Does the currently logged in user have the same ID?
You can always remove the third check by overriding the verify method in your VerificationController like so:
public function verify(Request $request)
{
$userId = $request->route('id');
$user = App\User::findOrFail($userId);
if ($user->markEmailAsVerified()) {
event(new Verified($user));
}
return redirect($this->redirectPath())->with('verified', true);
}

Related

How to unit test if the user is being redirected to login page if email not validated

I'm trying to write some unit tests for my code, I'm just learning how to write tests and I'm new to Laravel. Basically, I have a simple project, where the user should register and after validating their email, they are redirected to the landing page. In order to avoid some infinite redirects and bugs, I added a few lines of code that prevents the user to redirect himself to the landing page before verifying their email. So this is what I have in the controller file :
public function index(): View|RedirectResponse
{
if (auth()->user()->email_verified_at)
{
return view('landing.country', [
'countries' => Countries::all(),
]);
}
auth()->logout();
return redirect(route('home'));
}
So, If the user hasn't verified his email, if he attempts to view the blade only accessible to authenticated users, I'm logging him out and redirecting to the login page. Even though writing this was quite simple, I'm having a hard time coming up with a proper way to test this. How should I be able to approach testing this particular logout section? I have to test if the user attempts to enter auth only blade and check if he is being logged out and redirected to input page? Any help would be appreciated, currently I've been trying something like this in my test units :
public function test_if_user_is_logged_out()
{
User::factory()->create();
$user = User::first();
Auth::logout();
$response = $this->actingAs($user)->get(route('landing.worldwide'));
$response->assertRedirect(route('home'));
}
But obviously it doesn't work. I'd be forever thankful for any suggestions and help!
You could update your test to something like:
public function test_an_unverified_user_is_logged_out_and_redirected()
{
$user = User::factory()->unverified()->create();
$this->actingAs($user)
->get(route('landing.worldwide'))
->assertRedirect(route('home'));
$this->assertGuest();
}
unverified() is a method that is included with fresh installs of Laravel.
If you don't have unverified() in your UserFactory, you can simply change the user create call to be:
$user = User::factory()->create([
'email_verified_at' => null,
]);

How to show views to users that was not registered in Laravel?

I am using Laravel make:auth command and email verification of user. Now facing an issue that when any one visiting my web site the authentication will raise and redirect user to login page if the user not register it should register first and verify email before starting to visit my web site. I am wanted to show all my routs to any one without login or without registration. And also i need to verify user email when they register. it showing the routs to only registered and login users, but I am wanted to show every one.
public function __construct()
{
$this->middleware(['auth' => 'verified']);
}
after verifying email it shows the routs.
if I comment this code it will work fine but also i need to verify email.
this depends on how protective are you with the data to be shown to the visitors.
one method you can try is to use no middleware for those routes you want all of the viewers to visit and use a simple if else condition to show the respective view:
public function __construct()
{
// no middleware
}
public funtion index() {
if(Auth::check()) {
return view('logged.blade.php'); // for logged in users
}
return view('general.blade.php'); // for all visitors
}

Laravel 5.7 - How can I show email verification redirect page when the user is not logged in?

Laravel Version: 5.7.10
PHP Version: 7.2.10
Database Driver & Version: MySql 8.0.11
I am having new users verify their email address before I send them first time login credentials. They receive the verification email, and verification works. However, the page that they are supposed to be redirected to afterwards does not appear. The home page appears instead. Next time they log in, they are taken the the post-verification page. There is no auth middleware set on the route, and I can reach the post-verification page just fine when not logged in.
I set the redirect page in VerificationController with protected $redirectTo = '/verified'. And this is working, just not until the user logs in.
Via your issue from github. Simply change
$this->middleware('auth');
to
$this->middleware('auth')->except('verify');
Change middleware as commented by luminoslty, and also change VerifiesEmail.php from
public function verify(Request $request)
{
if ($request->route('id') == $request->user()->getKey() &&
$request->user()->markEmailAsVerified()) {
event(new Verified($request->user()));
}
return redirect($this->redirectPath())->with('verified', true);
}
to
public function verify(Request $request)
{
$user = User::find($request->route('id'));
if ($user) {
$user->markEmailAsVerified()) {
event(new Verified($user));
}
return redirect($this->redirectPath())->with('verified', true);
}

laravel 5.1 check if a session is expired

I'm working on a laravel 5.1 application and I want to check if a user session has expired after each request, in order to redirect the user to the login page.
in order to do so I have created a middleware that runs on every request, the handle function looks like this
public function handle($request, Closure $next)
{
if(session_status() === PHP_SESSION_NONE)
{
return redirect()->guest('login');
}
return $next($request);
}
this does not seem to work correctly, because when I type 'localhost:8000' in google chrome it says 'localhost redirected you too many times', I guess it is because the session have not been started since the user is not logged in, so... is there any better way to do this checking?
You can disable middleware in certain routes. by adding the login route to the excepted_urls array. For example, add the following at the beginning of the class:
protected $except_urls = [
'login'
];
or you can disable it in your web.php/routes.php depending of the version of Laravel you're using by employing route grouping

Hesto multi-auth guard is not redirecting where page was before after login

I am on page : http://laravel.dev/lists
which shows me list of item
Item 1
Item 1
Item 1
but above item can only accessible to authorized user
Issue
when i click on Item 1 so if user is not logged in then it goes to http://laravel.dev/login then user proceed to login but after login it must redirect to http://laravel.dev/lists/1 but it is redirecting to http://laravel.dev/home.
I have tried
public function showLoginForm()
{
session()->put('url.intended',url()->previous());
// or
session()->put('url.intended', url()->current());
return view('user.auth.login');
}
but above is not working, it is redirecting to http://laravel.dev/lists
laravel own auth is working fine.
but hesto multi-auth is not working
I am using this Hesto-multi-auth package for multiple authentication like admin, user, employee
is there any solution for this
If you're using the default user Authentication system in Laravel, and if you added auth middleware in your routes, it's automatically done by Laravel.
In your routes:
Route::group(['middleware' => 'auth'], function() {
. . . Your Routes goes Here
});
You can take a look at the docs https://laravel.com/docs/5.4/authentication#protecting-routes
You can use middleware. For example :
Route::get('lists/{id}', function () {
//
})->middleware('auth');
Please refer here : https://laravel.com/docs/5.5/middleware for more information
First of all find the place where you redirect unauthenticated user to the login page (probably in app/Exceptions/Handler.php function unauthenticated) and save to the session current url ("/lists/1") before redirect.
Afrer that add in your Auth controller property $redirectTo and fill it from the session or define method redirectTo (method also nust return url).
Out of the box laravel redirects to /home because of trait /Illuminate/Foundation/Auth/RedirectsUsers.
You can try this idea: After you've successfully logged in, you'll be redirected to the page you want (e.g. http://laravel.dev/lists/1)
Auth::attempt(...);
return redirect()->route('the name of your route here where redirected to http://laravel.dev/lists/1');
What you had tried in the showLoginForm() won't be work, because neither url()->previous() nor url()->current() you specified were not the url you want.
Solution:
Changed in the middleware:
public function handle($request, Closure $next, $guard = 'user')
{
if (!Auth::guard($guard)->check()) {
session()->put('url.intended', \URL::current()); // Add this line
return redirect('user/login');
}
return $next($request);
}

Categories