Issues Migrating Forgot Password Tutorial Code from Slim 2 to Slim 3


#1

Hello all,
First of all, am a Newbie in PHP and Slim so please be mild with me.

I tried migrating the forgot password tutorial in php authentication system from Slim 2 to Slim 3 and am stuck in loop now.
Has anyone succeeded in migrating it and if yes, can you please share or help me figure out what is wrong with my code.

I am trying to pass some arguments from my getResetPassword get method to postResetPassword post method but if I var_dump the “$args” variable in my postResetPassword post method, it gives me an empty array.

PasswordResetController.php

<?php
/**
 * Created by PhpStorm.
 * User: Ivan
 * Date: 25/03/2017
 * Time: 04:38
 */
namespace App\Controllers\Auth;
use App\Controllers\Controller;
use \App\Models\User;



class PasswordResetController extends Controller
{

    public function getResetPassword($request, $response, $args)
    {
        $email = $request->getParam("email");
        $identifier = $request->getParam("identifier");
        $hashedIdentifier = $this->hash->hash($identifier);

        $user = User::where("email", $email)->first();


        if (!$user) {
            $this->flash->addMessage("error", "User with specified reset link not found.");
            return $response->withRedirect($this->router->pathFor("home"));

        }

        if ((!$user->recover_hash) || (!$this->hash->hashCheck($user->recover_hash, $hashedIdentifier))) {

            $this->flash->addMessage("error", "There is a problem resetting your password. ensure that you copied the reset link correctly or that you have not used this link before.");
            return $response->withRedirect($this->router->pathFor("home"));

        }

        $args = ["email" => $user->email, "identifier" => $identifier];

        return $this->view->render($response, "auth/password/reset.twig", $args);

    }

    public function postResetPassword($request, $response, $args)
    {
        var_dump($args); /* unfortunately gives an empty array */
        die();
        $email = $request->getParam("email");
        $identifier = $request->getParam("identifier");
        $hashedIdentifier = $this->hash->hash($identifier);

        $user = User::where("email", $email)->first();


        if (!$user) {
            $this->flash->addMessage("error", "User with specified reset link not found.");
            return $response->withRedirect($this->router->pathFor("home"));

        }

        if ((!$user->recover_hash) || (!$this->hash->hashCheck($user->recover_hash, $hashedIdentifier))) {

            $this->flash->addMessage("error", "There is a problem resetting your password. ensure that you copied the reset link correctly or that you have not used this link before.");
            return $response->withRedirect($this->router->pathFor("home"));

        }

        die("all ok");

    }


}

routes.php


<?php
/**
 * Created by PhpStorm.
 * User: Ivan
 * Date: 17/03/2017
 * Time: 02:03
 */

/* .......... snipped some code here */

    $app->get("/auth/password/reset", "PasswordResetController:getResetPassword")->setName("auth.password.reset");

    $app->post("/auth/password/reset", "PasswordResetController:postResetPassword");

reset.twig

{% extends "templates/app.twig" %}

{% block title %} Change Password {% endblock %}

 {% block content %}

     <section id="register">
         <div class="container">
             <div class="row text-center">
                 <div class="col-md-6 col-md-offset-3">
                     <div class="contact-heading">
                         <h2 class="title-one">Reset Password</h2>
                     </div>
                 </div>
             </div>
         </div>

         <div class="container">
             <div class="row text-center">

                 <div class="col-md-6 col-md-offset-3">

                     <div class="panel panel-default">

                         <div class="panel-heading">


                             <div class="panel-body">

                                 <form action="{{ path_for("auth.password.reset") }}" method="post" autocomplete="off">

                                     <div class="form-group{{ errors.new_password ? " has-error" : "" }}">
                                         <input type="password" name="new_password" class="form-control name-field" required="required" placeholder="New Password">
                                         {% if errors.new_password %}
                                             <span class="help-block">{{ errors.new_password | first }}</span>
                                         {% endif %}
                                     </div>

                                     <div class="form-group{{ errors.confirm_password ? " has-error" : "" }}">
                                         <input type="password" name="confirm_password" class="form-control name-field" required="required" placeholder="Confirm Password">
                                         {% if errors.confirm_password %}
                                             <span class="help-block">{{ errors.confirm_password | first }}</span>
                                         {% endif %}
                                     </div>

                                     <center>
                                         <button type="submit" class="btn btn-primary">Reset password</button>
                                     </center>

                                     {{ csrf.field | raw }}

                                 </form>

                             </div>

                         </div>

                     </div>

                 </div>


             </div>
         </div>
     </section>

 {% endblock %}

can anyone please help me.
I have stuck in this for some days now.
Thanks.


#2

I think you are confusing named placeholders in a route ($args) with data coming in the request body $request->getAttribute('...')

If your route looks like

$app->patch("user/{id}", "UsersController:update");

Then in this case $args['id] will contain the route argument with the user id. In the body of the request you will get the data being updated. Like $newEmail = $request->getAttribute('email');

So in your example, that route doesn’t have any placeholders, so $args will be empty.


#3

first of all,
thanks for replying but I don’t seem to understand and I apologize for that.

let me explain better

If the user requests for a password reset, a url is generated and sent to the users email,
this url contains the user email and identifier.
this parameters are verified in the getResetPassword method after the user clicks the link from the email and renders the reset form.
my problem is how to pass some parameters like the email and identifier to the postResetPassword method from the getResetPassword method after rendering the reset form.


#4

That doesn’t sound like the correct logic to me, but to answer the question, ff you are in the getResetPassword method and you wish to call the postResetPassword method, you would call it like this.

$this->postPasswordReset($user->email, $identifier);

And your `postResetPasswordMethod would look like this.

public function postResetPassword($email, $identifier) {
    // do something with $email and $identifier
}

But when your form is submitted it will hit the second route you have listed and call the postResetPassword method directly, which doesn’t seem to be what you want.

Your original code looks okay except in the postResetPassword method $args should be empty because that route doesn’t have any placeholders.


#5

So please what is the correct logic?
Because what I want to achieve is

  1. The user clicks the link sent to the email. This link contains the user email and identifier.
    This link calls the getResetPassword route, does some housechecking and if passed renders the reset.twig form.

  2. After the user enters the new password and submits the form, it should call postResetPassword route (am I correct?)

  3. in this postResetPassword route, (now here is where my problem lies), I need the email and identifier from the getResetPassword route.
    How do I get it because it is not available in request parameter of that route.

Thanks a lot for your time and sorry if I sound stupid.
New to php and slim.


#6

Keep in mind I haven’t actually tried running your code. But based on what you’ve described you want to do and what I see in your code, I think you are close.

The user fills out the form at reset.twig. When they submit, it creates a POST request to /auth/password/reset. (I’d would have named the post route and used that in the template, but it will make no difference.)

When you get to the postResetPassword method, $args will be empty because there are no named placeholders for that route. However the $request body will have data submitted in the form based on the names of the form elements.

public function postResetPassword($request, $response, $args) {
    $newPassword = $request->getAttribute('new_password');
    $confirmPassword = $request->getAttribute('confirm_password');
    // You don't have $email and $identifier because you haven't sent them in the form, nor are they an argument of the route.

Since it looks like you passed the email and identifier to the form, you could include them has hidden fields in the form so they do get submit. But you will need to decide if that is secure or not.

Also I when you pass the data to the form I woudn’t name it $args to avoid confusion with the route placeholder arguments. Perhaps call it $data.

$data = ["email" => $user->email, "identifier" => $identifier];
return $this->view->render($response, "auth/password/reset.twig", $data);

#7

Thanks for replying.
I didn’t pass the email and identifier to the form.
Please how do I do that? (whether as hidden field or not).
All I need is to be able to access the email and identifier in the postResetPassword route/method.
I don’t care how that happens (secure or not) as long it works.
Thanks


#8

In your reset.twig, add in some hidden fields. (It looks like you’re already passing that data to the view.)

<input type="hidden" name="email" value="{{ email }}">
<input type="hidden" name="identifier" value="{{ identifier }}">

Those will be posted along with new_password and confirm_password to your postResetPassword method and you should be able to get them from the request object like this.

$email = $request->getAttribute('email');
$identifier = $request->getAttribute('identifier');

#9

finally it works.
I feel like giving you a big care bear hug and two bottles of vodka.
you are my hero.
Thanks a lot.
I really appreciate it.
you don’t know how much this means to me. :smile: