We have moved our forum to GitHub Discussions. For questions about Phalcon v3/v4/v5 you can visit here and for Phalcon v6 here.

Parameter empty on POST

I'm busy making a password reset function/action but for some reason the token i pass is empty when I post to the action, what am i missing.

this is the action

public function resetAction($token)
{
       // When I open the URL with a token i.e. ../auth/reset/181c5b31-a138-4d5c-b59f-80f17b6adffd it works
       if(!$token) {
            $this->flash->error('Request a reset token first');
            return $this->dispatcher->forward(["controller" => "auth", "action" => "requestreset" ]);
        }
        $this->view->setVar('form', new ResetPasswordForm(null, ['edit' => false]));

        if ($this->request->isPost()) {
            $token // is just empty even though the URL is the same
        }
}

Any help is appreciated!



13.8k
Accepted
answer
edited May '17

Ok figured it out myself, was hoping for a more neat solution since this feels as a work-around. I'm now passing the GET parameter via a hidden form field.

Controller Action

if($this->request->isPost()) {
        $token = $this->request->getPost('token');
}

if(!$token) {
    $this->flash->error('Request a reset token first');
     return $this->dispatcher->forward(["controller" => "auth", "action" => "requestreset" ]);
}
$this->view->setVar('form', new ResetPasswordForm(null, ['edit' => false, 'usertoken' => $token]));

Form

use Phalcon\Forms\Element\Hidden;

$token = new Hidden(
     "token", [
         "class" => "hidden",
         "value" => $options["usertoken"]
       ]
);
$this->add($token);

In your example, you need your Router object/service to pass this argument to the route handler. So actually it is being sent via REQUEST_URI superglobal, which your web server should populate.

If you need to read data from POST, you do it using Request object/service.

First debug what your form is actually sending to the handler by Web developer tools -> Networking. Make sure you're actually passing this URL parameter. Then, check how your Router is configured and routes in general.



13.8k
edited Jun '17

In your example, you need your Router object/service to pass this argument to the route handler. So actually it is being sent via REQUEST_URI superglobal, which your web server should populate.

If you need to read data from POST, you do it using Request object/service.

First debug what your form is actually sending to the handler by Web developer tools -> Networking. Make sure you're actually passing this URL parameter. Then, check how your Router is configured and routes in general.

I'm using the default router controller/action/parameters. I tried to get the values via $this-request->getPost() aswell as some other possible solution i came around.

How can I achive the first part you mention "In your example, you need your Router object/service to pass this argument to the route handler" ? Do you have a example which i can dive into?



13.8k

I tried the above via the router aswell as described in the link before making the work-around, but it remained empty. I'll read it again maybe i missed something.

I'm running into another weird issue though. I'm checking the csrf token the same way I'm doing it in other actions where it works, but in the resetAction its empty. Also when i var_dump($this->request->getPost()) it appears empty.

In the HTML code the the hidden crsf field appears as it should be though:

<input type="hidden" id="csrf" name="csrf" value="K1RTMld3cVkwRS9rRzQyL2RVV0hiQT09">

This is my controller

<?php
use Phalcon\Mvc\Controller;
use PhalconTime\Forms\LoginForm;
use PhalconTime\Forms\RequestResetForm;
use PhalconTime\Forms\ResetPasswordForm;
use PhalconTime\Models\User;
use Phalcon\Security;
use Phalcon\Security\Random;
use Phalcon\Mvc\Url;

class AuthController extends Controller
{

    /**
     * The start action, it shows the login form
     */
    public function indexAction()
    {
        $this->view->setVar('form', new LoginForm(null, ['edit' => false]));
    }

    /**
     * The login action, validates user
     */
    public function loginAction()
    {

        $username = $this->request->getPost('username');
        $password = $this->request->getPost('password');
        $csrf     = $this->request->getPost('csrf');

        $user = User::findFirstByName($username);

        if($user) {
            if($this->security->checkToken($csrf, $this->security->getSessionToken()) && $this->security->checkHash($password, $user->password)) {
                $this->session->set('auth-identity', [
                    'id' => $user->id,
                    'username' => $user->name,
                    'image' => $user->image
                ]);
                return $this->dispatcher->forward(["controller" => "project", "action" => "index"]);
            }
            else {
                $this->flash->error('User/Password combination incorrect');
                return $this->dispatcher->forward(["controller" => "auth", "action" => "index"]);
            }
        }
        else {
            $this->flash->error('User not found');
            return $this->dispatcher->forward(["controller" => "auth", "action" => "index"]);
        }
    }

    /**
     * The logout action, remove user session
     */
    public function logoutAction()
    {
        $this->session->remove('auth-identity');

        return $this->dispatcher->forward([
            "controller" => "auth",
            "action" => "index"
        ]);
    }

    /**
     * The password forgotten action
     */
    public function requestResetAction()
    {
        $this->view->setVar('form', new RequestResetForm(null, ['edit' => false]));
    }

    /**
     * the create token action
     */
    public function createTokenAction()
    {
        if (!$this->request->isPost()) {
            return $this->dispatcher->forward(["controller" => "auth", "action" => "requestreset"]);
        }

        $csrf  = $this->request->getPost('csrf');
        $email = $this->request->getPost("email", "string");
        $user  = User::findFirstByEmail($email);

        if($this->security->checkToken($csrf, $this->security->getSessionToken())) {
            if (!$user) {
                $this->flash->error('User not found');
                return $this->dispatcher->forward(["controller" => "auth", "action" => "requestreset" ]);
            }

            $random      = new Random();
            $uuid        = $random->uuid();
            $user->token = $uuid;

            if($user->save()) {

                $headers = "From: ".$_SERVER['SERVER_NAME']."\n";
                $headers .= "X-Mailer: PHP/" . phpversion() . "\n";
                $headers .= "MIME-Version: 1.0\n";
                $headers .= "Content-Type: text/html; charset=utf-8\n";
                $headers .= "Content-Transfer-Encoding: 8bit\n";

                $subject = 'Reset password for '.$_SERVER['SERVER_NAME'];

                $content = $this->view->getRender('emails', 'reset-password', ['token' => $uuid, 'url' => $this->config->application->domainUri]);

                if(mail($email, $subject, $content, $headers)) {
                    $this->flash->success('An e-mail with instruction to reset the password has been send');
                    return $this->dispatcher->forward(["controller" => "auth", "action" => "index" ]);
                }

                $this->flash->error('Could not send e-mail');
                return $this->dispatcher->forward(["controller" => "auth", "action" => "requestreset" ]);

            }

        }

        $this->flash->error('Something went wrong');
        return $this->dispatcher->forward(["controller" => "auth", "action" => "requestreset" ]);
    }

    /**
     * The password reset action
     */
    public function resetAction($token)
    {

        if($this->request->isPost()) {
            $token   = $this->request->getPost('token');
            $csrf    = $this->request->getPost('csrf'); // is empty

            // @TODO csrf is empty on POST
            //if($this->security->checkToken($csrf, $this->security->getSessionToken())) {
                $user  = User::findFirstByToken($token);

                if (!$user) {
                    $this->flash->error('Token is not valid, request a new token');
                    return $this->dispatcher->forward(["controller" => "auth", "action" => "requestreset" ]);
                }

                // @TODO check if form is valid
                $password       = $this->request->getPost("password");
                $user->password = $this->security->hash($password);
                $user->token    = NULL;

                if ($user->save() == false) {
                    foreach ($user->getMessages() as $message) {
                        $this->flash->error($message);
                    }
                    $this->flash->error('Something went wrong, request a new token');

                    return $this->dispatcher->forward(["controller" => "auth", "action" => "requestreset"]);
                }
                $this->flash->success('Password changed');

                return $this->dispatcher->forward(["controller" => "auth", "action" => "index" ]);

            //}
        }

        if(!$token) {
            $this->flash->error('Request a reset token first');
            return $this->dispatcher->forward(["controller" => "auth", "action" => "requestreset" ]);
        }
        $this->view->setVar('form', new ResetPasswordForm(null, ['edit' => false, 'usertoken' => $token]));

    }

}

And the form object

<?php

namespace PhalconTime\Forms;

use Phalcon\Forms\Form;
use Phalcon\Forms\Element\Text;
use Phalcon\Forms\Element\Hidden;
use Phalcon\Forms\Element\Password;
use Phalcon\Validation\Validator\File as FileValidator;
use Phalcon\Validation\Validator\PresenceOf;
use Phalcon\Validation\Validator\Identical;
use Phalcon\Validation\Validator\StringLength;
use Phalcon\Validation\Validator\Confirmation;

class ResetPasswordForm extends Form
{
    /**
     * Initialize the reset password form
     *
     * @param mixed $entity
     * @param array $options
     */
    public function initialize($entity = null, $options = [])
    {

        $token = new Hidden(
            "token", [
                "class" => "hidden",
                "value" => $options["usertoken"]
            ]
        );
        $this->add($token);

        // Password
        $password = new Password(
            "password",
            [
                "placeholder" => "Password",
                "class"       => "form-control",
            ]
        );
        $password->setLabel("Password");
        $password->setFilters(
            [
                "striptags",
                "string",
            ]
        );
        $password->addValidators(
            [
                new PresenceOf(
                    [
                        "message" => "Password is required",
                    ]
                ),
                new StringLength(
                    [
                        'min' => 8,
                        'messageMinimum' => 'Password is too short. Minimum 8 characters'
                    ]
                ),
                new Confirmation(
                    [
                        'message' => 'Password doesn\'t match confirmation',
                        'with' => 'passwordRepeat'
                    ]
                )
            ]
        );
        $this->add($password);

        // Password repeat
        $passwordRepeat = new Password(
            "passwordRepeat",
            [
                "placeholder" => "Password repeat",
                "class"       => "form-control",
            ]
        );
        $passwordRepeat->setLabel("Password repeat");
        $passwordRepeat->setFilters(
            [
                "striptags",
                "string",
            ]
        );
        $passwordRepeat->addValidators(
            [
                new PresenceOf(
                    [
                        "message" => "Password repeat is required",
                    ]
                )
            ]
        );
        $this->add($passwordRepeat);

        // CSRF
        $csrf = new Hidden('csrf');
        $csrf->addValidator(new Identical(
            [
                'value' => $this->security->getSessionToken(),
                'message' => 'CSRF validation failed'
            ])
        );
        $csrf->clear();
        $this->add($csrf);

    }

}