I am trying to pass a pageTitle variable to the password reset template (/resources/views/auth/passwords/reset.blade.php) in Laravel 5.3 in the following way:
return view('auth.passwords.reset')
->with('pageTitle', 'Change title')
->with(['token' => $token, 'email' => $request->email]);
this goes in the showResetForm method inside ResetsPasswords trait - and it doesn't work. Google doesn't come up with any helpful results. I've tried removing the line:
->with(['token' => $token, 'email' => $request->email]);
but it still doesn't work. I've also tried
$pageTitle = 'Change me';
return view('auth.passwords.reset', compact('pageTitle'));
but it doesn't work. Also, I've realized that the ResetsPassword trait is found in the vendor folder so it's a bad idea to change the code there, how do you suggest I do this instead? Can I overwrite the showResetForm method somewhere? - I found that for the registration trait I can put the showRegistrationForm in the RegistrationController and pass whatever variables I want to the view there; however that doesn't work for the ResetPasswordController
EDIT:
Here is the whole method from the ResetsPasswords trait, as requested:
/**
* Display the password reset view for the given token.
*
* If no token is present, display the link request form.
*
* #param \Illuminate\Http\Request $request
* #param string|null $token
* #return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function showResetForm(Request $request, $token = null)
{
return view('auth.passwords.reset')->with(['token' => $token, 'email' => $request->email]);
}
Try using compact
return view('auth.passwords.reset', compact('token', 'email', etc..));
return view('auth.passwords.reset')
->with(['pageTitle' => 'Change Title','token' => $token, 'email' => $request->email]);
have you tried it like this?
EDIT
$pageTitle = 'Change Title';
$token = 'token';
$email = 'email';
return view('auth.passwords.reset', compact('pageTitle','token','email'));
The problem was I was editing the wrong trait, I need to edit the SendsPasswordResetEmails trait and not the ResetsPassword.
Related
I write here after many attempts but my problem isn't solved yet.
I want to create a test using PHPUnit on Laravel my class has function described like below:
public function test_not_connected_user_can_not_create_new_task() {
$this->withoutExceptionHandling();
//Given we have a task object
$task = Task::factory()->make();
// When unauthenticated user submits post request to create task endpoint
// He should be redirected to login page
$this->post('/tasks/store',$task->toArray())
->assertRedirect(route('login'));
}
Here is my route:
Route::post('/tasks/store', [App\Http\Controllers\TaskController::class, 'store'])
->name('store');
And my controller functions:
public function __construct() {
$this->middleware('auth')->except(['index','show']);
}
/**
* Store a newly created resource in storage.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function store(Request $request) {
$task = Task::create([
'title' => $request->get('title'),
'description' => $request->get('description'),
'user_id' => Auth::id()
]);
return redirect()->route('show', [$task->id]);
}
We have a middleware here to manage authentication.
When I run the test:
vendor/bin/phpunit --filter test_connected_user_can_create_new_task
I get this error:
Tests\Feature\TasksTest::test_not_connected_user_can_not_create_new_task
Illuminate\Auth\AuthenticationException: Unauthenticated.
And it's pointing to this line:
$this->post('/tasks/store', $task->toArray())
The expected behavior is that it should redirect to login but here the test fail and I can't see why.
Thanks
The issue is super easy to fix. You have $this->withoutExceptionHandling(); and that is literally throwing the error, what you want is to catch it and let everything continue. To do so, remove that line of code and your test will work.
I'm using laravel breeze as auth scaffolding package I want to know How can I
create two diffirent registration form for two User Types here is a simple explanation of hwat I want to achieve:
resources/auth/developer :
developer-register.blade.php
resources/auth/designer :
designer-register.blade.php
if the Visitor choose to register as "developer" it will display a diffirent form. and same thing for if the Visitor choose to register as "designer" it will display a diffirent form with fields.
I wish you understand what I want to achieve with this easy explanation.
Ok, so i've not used laravel/breeze myself (yet) but it shouldn't be much different from doing it in standard Laravel!
Views
By default, it looks like the breeze scaffolding is going to hit a create() method on the RegisteredUserController which will return a single view like so:
RegisteredUserController.php
/**
* Display the registration view.
*
* #return \Illuminate\View\View
*/
public function create()
{
return view('auth.register');
}
You have a few options here:
Replace this view with another
Add some logic to change the view which is returned based on the request being made (you can inject a Request object into the route like any other)
public function create(Request $request)
{
if ($request->has('developer')) {
return view('auth.developer-register');
} else {
return view('auth.designer-register');
}
}
Keep the original auth.register view and handle the logic in the blade template.
Registration
The forms on each of your registration pages will have an action that points to a controller route. This will likely be the RegisteredUserController within which you will find a store() method that handles the creation of a User model.
RegisteredUserController.php
/**
* Handle an incoming registration request.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\RedirectResponse
*
* #throws \Illuminate\Validation\ValidationException
*/
public function store(Request $request)
{
$request->validate([
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|confirmed|min:8',
]);
Auth::login($user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password),
]));
event(new Registered($user));
return redirect(RouteServiceProvider::HOME);
}
As you can see, this store() method is handling the creation of a User model and then authenticating it before redirecting the user to the home route.
What you could do, is check the request for the the requested user type and then use a switch statement to change the type of use being created.
switch ($request->get('user_type'))
case 'developer':
$user = Developer::create([ /* add details here */ ]);
break;
case 'designer':
$user = Designer::create([ /* add details here */ ]);
break;
Auth::login($user);
I hope this will at least inspire you with your own solution!
Heyo!
I know it's a common problem people having problems with custom providers and web service authentication. I'm spending hours trying to figure out how to do that but I'm almost freaking out.
So, the thing is: I'm using the Symfony Firewalls with a custom UserProvider and a AbstractGuardAuthenticator as well. The problem is in the loadUserByUsername($username) function inside the UserProvider implementation.
For security reasons I can't retrieve the user password from Parse (my web service), and the loadUserByUsername($username) function asks for that. Even in the documentation about how to create a custom user provider using web services they are retrieving the user password from the database.
So what's the solution in that case? What can I do when I don't have access to the user password?
My current code is something like that:
$app['app.authenticator'] = function () {
return new Authenticator($app);
};
$app['security.firewalls'] = array(
'login' => array(
'pattern' => '^/login/$',
),
'secured' => array(
'pattern' => '^.*$',
'form' => array('login_path' => '/login/', 'check_path' => '/login/auth/'),
'logout' => array('logout_path' => '/logout/', 'invalidate_session' => true),
'guard' => array(
'authenticators' => array(
'app.authenticator'
),
),
'users' => function () use ($app) {
return new UserProvider($app);
},
)
);
The Authenticator.php is quite big code because extends the AbstractGuardAuthenticator class. But I'm basically using this one from Symfony docs. The only thing Is that I'm sending to the UserProvider class the username AND the password as well, because that way I can check if the user and password are right. Like this:
public function getUser($credentials, UserProviderInterface $userProvider) {
return $userProvider->loadUserByUsername($credentials);
}
And my UserProvider class is the default one, I'm just checking inside the loadUserByUsername function if the credentials comming from my Authenticator are right. Something like this:
public function loadUserByUsername($credentials) {
$encoder = new BCryptPasswordEncoder(13);
try {
$user = ParseUser::logIn($credentials['username'], $credentials['password']);
} catch (ParseException $error) {
throw new UsernameNotFoundException(sprintf('Invalid Credentials.'));
}
return new User($credentials['username'], $encoder->encodePassword($credentials['password'], ''), explode(',', 'ROLE_USER'), true, true, true, true);
}
The problem is: after the login (everything with the login is working fine), Silex calls the loadUserByUsername function in every page which needs to be secured, but just sending the username parameter.
So basically, I don't know what to do guys. I'm really trying to figure out how to get this thing working.
Thanks for your help!
I have a similar implementation and this issue is well known. In my user provider I have the methods loadUserByUsername($username) and refreshUser(UserInterface $user). Since I have the same issue like you, I don't check the user in loadUserByUsername but simple return a new Object with only the username in it to not disturb the flow. loadUserByUsername doesn't make sense for external APIs, so I simply jump over it. The method refreshUser is either beeing called on every request, this is usefull.
In your AbstractGuardAuthenticator you have the method createAuthenticatedToken, which returns an token. There you should have the full authentificated user:
abstract class AbstractGuardAuthenticator implements GuardAuthenticatorInterface
{
/**
* Shortcut to create a PostAuthenticationGuardToken for you, if you don't really
* care about which authenticated token you're using.
*
* #param UserInterface $user
* #param string $providerKey
*
* #return PostAuthenticationGuardToken
*/
public function createAuthenticatedToken(UserInterface $user, $providerKey)
{
//do login stuff
//save password in user
return new PostAuthenticationGuardToken(
$user,
$providerKey,
$user->getRoles()
);
}
}
Then, I would't use loadUserByUsername but refreshUser instead. Both are called on every request:
/**
* Don't use
* #codeCoverageIgnore
* #param string $username
* #return User
*/
public function loadUserByUsername($username)
{
return new User($username, null, '', ['ROLE_USER'], '');
}
/**
* Refresh user on every subrequest after login
* #param UserInterface $user
* #return User
*/
public function refreshUser(UserInterface $user)
{
$password = $user->getPassword();
$username = $user->getUsername();
//login check
}
There is a very similar question, but I couldn’t comment on it or follow the question author’s advice to fix my problem.
Password reset was already set up and worked, I got back to it to check translation strings, and started getting this error: Fatal error: Call to a member function getEmailForPasswordReset() on null (View: /home/vagrant/code/ecc/resources/views/emails/password.blade.php)
I didn’t use source control for the project, so I couldn’t find out when exactly it stopped working. The only things that I changed in my password controller, were getSendResetLinkEmailSuccessResponse() and getSendResetLinkEmailFailureResponse() to AJAXify my forms. Almost the same exact code runs in another project, but on the same Homestead, and it works. I checked the whole chain of calls, and unsurprisingly, there's nothing wrong with it—a user object becomes null somewhere during view rendering:
8. at Mailer->getView('emails.password', array('token' => 'd81d3190958330e2a4e0552db07a1efc80d1768eddde0447f8efb9e588719ff4', 'user' => object(User), 'message' => object(Message))) in Mailer.php line 323
4. at CompilerEngine->get('/home/vagrant/code/ecc/resources/views/emails/password.blade.php', array('__env' => object(Factory), 'app' => object(Application), 'errors' => object(ViewErrorBag), 'token' => 'd81d3190958330e2a4e0552db07a1efc80d1768eddde0447f8efb9e588719ff4', 'user' => null, 'message' => object(Message), 'locale' => 'en')) in View.php line 149
3. at PhpEngine->evaluatePath('/home/vagrant/code/ecc/storage/framework/views/469f1ba57eac0cc812c6c63e33641df67f64c100.php', array('__env' => object(Factory), 'app' => object(Application), 'errors' => object(ViewErrorBag), 'token' => 'd81d3190958330e2a4e0552db07a1efc80d1768eddde0447f8efb9e588719ff4', 'user' => null, 'message' => object(Message), 'locale' => 'en')) in CompilerEngine.php line 59
So, Mailer gets a user as a retreived object, but then it becomes null during view rendering. I tried reinstalling everything, vagrant destroy, dump-autoload, clear-compiled, removing all compiled views, I’m also not using Laravel Auto Presenter, or anything similar that tries to automatically wrap the user model in a decorator when it's passed into a view.
Can you please advise something? Thanks!
EDIT: Here’s my controller, as requested:
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ResetsPasswords;
class PasswordController extends Controller
{
use ResetsPasswords;
protected $subject;
protected $redirectTo = '/';
/**
* Create a new password controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('guest');
$this->subject = trans('emails.password_reset.subject');
}
/**
* Display the password reset view for the given token.
*
* If no token is present, display the link request form.
*
* #param \Illuminate\Http\Request $request
* #param string|null $token
* #return \Illuminate\Http\Response
*/
public function showResetForm($request, $token = null)
{
if (is_null($token)) {
abort(403, 'Unauthorized action.');
}
$email = $request->input('email');
return view('reset')->with(compact('token', 'email'));
}
/**
* Get the response for after the reset link has been successfully sent.
*
* #param string $response
* #return \Symfony\Component\HttpFoundation\Response
*/
protected function getSendResetLinkEmailSuccessResponse($response)
{
return response()->json([
'success' => true,
]);
}
/**
* Get the response for after the reset link could not be sent.
*
* #param string $response
* #return \Symfony\Component\HttpFoundation\Response
*/
protected function getSendResetLinkEmailFailureResponse($response)
{
return response()->json([
'success' => false,
]);
}
}
I use a view composer class to append a couple of variables to all views, one of them being $user that stores Auth::user(). Needless to say, this variable—which is obviously null, because a user is not logged in when it’s returned during password reset—overwrites a $user variable that Mailer passes further down the chain.
The solution is to either rename a $user variable in a view composer, use a custom password broker to overwrite emailResetLink() and change the variable name there, or pass an array of specific views in ComposerServiceProvider instead of a “*” as a wildcard.
I copied an example from the laravel documentation:
public function postResetPassword() {
$credentials = array('email' => Input::get('email'));
return Password::reset($credentials, function($user, $password) {
$user->password = Hash::make($password);
$user->save();
return Redirect::to('/');
});
}
But it seems that returning Redirect::to('/') doesn't work, because instead of home page I get an error which tells that controller method is not found.
But if I write the code this way:
$credentials = array('email' => Input::get('email'));
Password::reset($credentials, function($user, $password) {
$user->password = Hash::make($password);
$user->save();
});
return Redirect::back();
It works, though I can't understand how do I get session flash variables (actually I get them).
Another question is where are the rules about password length (6 chars) are written? Can I change them?
To answer why your first code example doesn't work is because if your look at your app/routes.php file you should see something along the lines of Route::get('/', 'HomeController#index');. The part that comes before the # symbol is the name of your controller while the part after it is the method that is being called in your controller when the route is requested. Make sure that method is defined.
After looking at the following. I think you should put the redirect inside of the closure you give as a return statement.
Then how you retrieve data that has been flashed to the session after redirecting the user you use the following Session::get('key');.
For your last question look at the following documentation.
Example:
$validator = Validator::make(
array('email' => Input::get('email'), 'password' => Input::get('password'), 'password_confirm' => Input::get('password_confirm')),
array('email' => 'required|unique:users,email|email', 'password' => 'required|min:3|max:20|same:password_confirm')
);
The second array passed is where you can modify the rules for the validator.
To answer your second question regarding changing the rules for password validation.
The Password facade extends PasswordBroker.php which has this function on line 208:
/**
* Set a custom password validator.
*
* #param \Closure $callback
* #return void
*/
public function validator(Closure $callback)
{
$this->passwordValidator = $callback;
}
Therefore, to override the default password validator, simply make this call from your controller:
Password::validator(function(){
//validator in here
});