getting intended target url without losing the values in middleware

Viewed 266

I have the following two settings page routes one for normal settings and the other for secured and admin related settings which uses the same middleware "password.confirm"

Route::get('/admin/settings',WebsiteInfoController::class,'edit'])->name('settings')->middleware('password.confirm');

Route::get('/settings',[WebsiteInfoController::class, edit'])->name('user.settings')->middleware('password.confirm');

This middleware redirects me to a second page where I have to enter password and then only i can get access to my intended page. In my middleware I have the following function. I want to make an additional check if the user is intending to access the admin related settings

public function store(Request $request)
 {
    $this->validate($request, [
        'secret_password' => ['sometimes','required'],
        'password' => ['required','password:web'],
    ]);      
    if(redirect()->intended()->getTargetUrl()==route('settings')){
        $secret_password =WebsiteInfo::first()->secret_password;
        if (!Hash::check($request->secret_password, $secret_password)) {
            throw ValidationException::withMessages([
                'secret_password' => __('auth.password'),
            ]);
        }
    }
   $request->session()->put('auth.password_confirmed_at', time());    
    return redirect()->intended(RouteServiceProvider::HOME);
});
});
    }

Everything works fine in this method but the intended URL is lost when all the check is performed and I am redirected to homepage instead of settings page. I also tried to save the URL in a variable and use it later in the redirect command like

$path=redirect()->intended()->getTargetUrl();

  if($path==route('settings')){
            $secret_password =WebsiteInfo::first()->secret_password;
            if (!Hash::check($request->secret_password, $secret_password)) {
                throw ValidationException::withMessages([
                    'secret_password' => __('auth.password'),
                ]);
            }
        }
       $request->session()->put('auth.password_confirmed_at', time());    
        return redirect()->intended($path);

This method works fine but it also loses the URL if the second validation fails and the user is redirected back to the confirm password page. Now when I try to perform the validation second time it again loses the intended URL and redirects me back to home page. i also tried the check with $request method.

 if($request->route()->named('settings')){
            $secret_password =WebsiteInfo::first()->secret_password;
            if (!Hash::check($request->secret_password, $secret_password)) {
                throw ValidationException::withMessages([
                    'secret_password' => __('auth.password'),
                ]);
            }
        }

This method however, is not able to detect the route in middleware and the validation check is not at all performed.

So, My question is how do i check for the intended URL and perform validation check without losing the intended URL even after multiple failed validation attempts?

1 Answers

Your method is all fine. You just used the wrong method to extract the target website URL. It is true that redirect()->intended()->getTargetUrl() gives you the target page URL but it also removes the target website URL from the session so when you finish performing the checks and want to redirect to the intended page there is no intended page URL found in the session and you get redirected to the default fall back URL. This is what the redirect function does

public function intended($default = '/', $status = 302, $headers = [], $secure = null) {
    $path = $this->session->pull('url.intended', $default);

    return $this->to($path, $status, $headers, $secure);
}

Here, the $request->route()->named('settings) method does not work since you are not directly interacting with your initial view but instead through a middleware view which does not send the intended page request. Use the following code and I guess you will be all fine with your validation attempts. It will work even after multiple failed login attempts.

public function store(Request $request) {
    $this->validate($request, [
        'secret_password' => ['sometimes','required'],
        'password' => ['required','password:web'],
    ]);
    $path=session()->get('url.intended', RouteServiceProvider::HOME);
    if($path==route('settings')) {
        $secret_password =WebsiteInfo::first()->secret_password;
        if (!Hash::check($request->secret_password, $secret_password)) {
            throw ValidationException::withMessages([
                'secret_password' => __('auth.password'),
            ]);
        }
    }
    $request->session()->put('auth.password_confirmed_at', time());    
    return redirect()->intended($path);
}
Related