Welcome to Abdul Malik Ikhsan's Blog

Create Login functionality in Expressive 3

Posted in expressive, Zend Framework by samsonasik on January 12, 2018

Zend Expressive 3 is not released yet released, and expressive session related components are in active development ready to use. However, we already can give them a try.

Use case

For example, we need simple login functionalities:

  1. Login Form
  2. Authentication process, read from DB
  3. Save authenticated value to Session

Setup

First, we can install the Zend Expressive 3 skeleton with the following command:

$ composer create-project "zendframework/zend-expressive-skeleton:^3.0.0" expressive3

Important Notes:

When prompted with service container requirement, choose zend-servicemanager
When prompted with template engine requirement, choose zend-view

There are components that can be installed via command:

$ cd expressive3
$ composer require \
     zendframework/zend-form:^2.11 \
     zendframework/zend-i18n:^2.7 \
     zendframework/zend-expressive-authentication:^1.0 \
     zendframework/zend-expressive-authentication-session:^1.0 \
     zendframework/zend-expressive-session:^1.0 \
     zendframework/zend-expressive-session-ext:^1.0

After above components installed, ensure that your config/config.php injected with ConfigProvider like below:

<?php
// config/config.php
$aggregator = new ConfigAggregator([
    // ... form requirements
    \Zend\I18n\ConfigProvider::class,
    \Zend\Form\ConfigProvider::class,
    \Zend\InputFilter\ConfigProvider::class,
    \Zend\Filter\ConfigProvider::class,
    \Zend\Hydrator\ConfigProvider::class,
    // ...

    // ... auth requirements
    \Zend\Expressive\Authentication\ConfigProvider::class,
    \Zend\Expressive\Authentication\Session\ConfigProvider::class,
    \Zend\Expressive\Session\ConfigProvider::class,
    \Zend\Expressive\Session\Ext\ConfigProvider::class,
    // ...
];

we can first setup database data, in this case, I tried with Postgresql:

$ createdb -Udeveloper expressive
Password:

$ psql -Udeveloper expressive
Password for user developer:

psql (10.1)
Type "help" for help.

expressive=# CREATE TABLE users(username character varying(255) PRIMARY KEY NOT NULL, password text NOT NULL);
CREATE TABLE

expressive=# CREATE EXTENSION pgcrypto;
CREATE EXTENSION

expressive=# INSERT INTO users(username, password) VALUES('samsonasik', crypt('123456', gen_salt('bf')));
INSERT 0 1

Above, I create database named “expressive”, create table named “users” with username and password field, insert sample data with pgcrypto extension for create hashed password of 123456 using blowfish.

Now, we can setup the authentication configuration at config/autoload/local.php as follows:

<?php
// config/autoload/local.php
return [

    'authentication' => [
        'pdo' => [
            'dsn'   => 'pgsql:host=localhost;port=5432;dbname=expressive;user=developer;password=xxxxx',
            'table' => 'users',
            'field' => [
                'identity' => 'username',
                'password' => 'password',
            ],
        ],
        'username' => 'username',
        'password' => 'password',
        'redirect' => '/login',
    ],

];

Then, we can map Zend\Expressive\Authentication\UserRepositoryInterface::class to Zend\Expressive\Authentication\UserRepository\PdoDatabase::class under alias and register Zend\Expressive\Authentication\AuthenticationInterface::class under factories config at config/autoload/dependencies.global.php :

<?php
// config/autoload/dependencies.global.php
return [

    'dependencies' => [
        'aliases' => [
            // ...
            Zend\Expressive\Authentication\UserRepositoryInterface::class =>
                Zend\Expressive\Authentication\UserRepository\PdoDatabase::class
        ],

        'factories' => [
            // ...
            Zend\Expressive\Authentication\AuthenticationInterface::class =>
                Zend\Expressive\Authentication\Session\PhpSessionFactory::class,
        ],
        // ...
    ],

];

For Session operations, we need Zend\Expressive\Session\SessionMiddleware middleware before routing middleware, so, in config/pipeline.php, we call pipe on it before $app->pipe(RouteMiddleware::class);:

// ...
$app->pipe(\Zend\Expressive\Session\SessionMiddleware::class);

// Register the routing middleware in the middleware pipeline
$app->pipe(RouteMiddleware::class);
// ...

as example: we want to redirect non-logged user to /login page, eg: at home page (/), we can register “home” routes config:

<?php
// config/routes.php
    $app->route('/', [
        \Zend\Expressive\Authentication\AuthenticationMiddleware::class,
        App\Handler\HomePageHandler::class,
    ], ['GET'], 'home');

Now run the php -S command:

$ php -S localhost:8080 -t public

When access ‘/’ page via localhost:8080, we should be redirected to /login page which currently a 404 page, nice!

Login Page

First, we create a LoginForm with username and password field like the following:

<?php
// src/App/Form/LoginForm.php
declare(strict_types=1);

namespace App\Form;

use Zend\Form\Element\Password;
use Zend\Form\Element\Text;
use Zend\Form\Form;
use Zend\InputFilter\InputFilterProviderInterface;

class LoginForm extends Form implements InputFilterProviderInterface
{
    public function __construct()
    {
        parent::__construct('login-form');
    }

    public function init()
    {
        $this->add([
            'type' => Text::class,
            'name' => 'username',
            'options' => [
                'label' => 'Username',
            ],
        ]);

        $this->add([
            'type' => Password::class,
            'name' => 'password',
            'options' => [
                'label' => 'Password',
            ],
        ]);

        $this->add([
            'name' => 'Login',
            'type' => 'submit',
            'attributes' => [
                'value' => 'Login',
            ],
        ]);
    }

    public function getInputFilterSpecification()
    {
        return [
            [
                'name' => 'username',
                'required' => true,
                'filters' => [
                    ['name' => 'StripTags'],
                    ['name' => 'StringTrim'],
                  ],
            ],

            [
                'name' => 'password',
                'required' => true,
                'filters' => [
                    ['name' => 'StripTags'],
                    ['name' => 'StringTrim'],
                ],
            ],
        ];
    }
}

We then can create a login page handler with inject it with login form with the following factory:

<?php
// src/App/Handler/LoginPageFactory.php
declare(strict_types=1);

namespace App\Handler;

use App\Form\LoginForm;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Container\ContainerInterface;
use Zend\Expressive\Template\TemplateRendererInterface;
use Zend\Form\FormElementManager;

class LoginPageFactory
{
    public function __invoke(ContainerInterface $container) : MiddlewareInterface
    {
        $template  = $container->get(TemplateRendererInterface::class);
        $loginForm = $container->get(FormElementManager::class)
                               ->get(LoginForm::class);

        return new LoginPageHandler($template, $loginForm);
    }
}

The LoginPageHandler itself can be initialized with :

<?php
// src/App/Handler/LoginPageHandler.php
declare(strict_types=1);

namespace App\Handler;

use App\Form\LoginForm;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Zend\Diactoros\Response\HtmlResponse;
use Zend\Diactoros\Response\RedirectResponse;
use Zend\Expressive\Authentication\UserInterface;
use Zend\Expressive\Session\SessionMiddleware;
use Zend\Expressive\Template\TemplateRendererInterface;

class LoginPageHandler implements MiddlewareInterface
{
    private $template;
    private $loginForm;

    public function __construct(
        TemplateRendererInterface $template,
        LoginForm                 $loginForm
    ) {
        $this->template  = $template;
        $this->loginForm = $loginForm;
    }

    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler) : ResponseInterface
    {
        $session = $request->getAttribute(SessionMiddleware::SESSION_ATTRIBUTE);
        if ($session->has(UserInterface::class)) {
            return new RedirectResponse('/');
        }

        $error = '';
        // handle authentication here Next

        return new HtmlResponse(
            $this->template->render('app::login-page', [
                'form'  => $this->loginForm,
                'error' => $error,
            ])
        );
    }
}

Above, we redirect to ‘/’ page when there is a session data as it already authenticated check. We are going to add authentication process next.

The Login form can be as simple as the following:

<?php // templates/app/login-page.phtml

echo $error;

$form->prepare();
echo $this->form($form);

We can register the LoginPageHandler at App\ConfigProvider::getDependencies() config:

<?php
// src/App/ConfigProvider.php
class ConfigProvider
{
    public function getDependencies() : array
    {
        return [
            'invokables' => [ /**/ ],
            'factories'  => [
                // ...
                Handler\LoginPageHandler::class => Handler\LoginPageFactory::class,
            ],
        ];
    }
}

The routing can be registered as follows with add \Zend\Expressive\Authentication\AuthenticationMiddleware::class for next middleware:

// config/routes.php
// ...
$app->route('/login', [
    App\Handler\LoginPageHandler::class,
    // for authentication next handling
    \Zend\Expressive\Authentication\AuthenticationMiddleware::class,
], ['GET', 'POST'],'login');

Above, we allow ‘GET’ and ‘POST’ in same ‘/login’ page.

Authentication process

Time for authentication process, we utilize Zend\Expressive\Authentication\AuthenticationMiddleware class that registered at the last entry at the /login route, we can accomodate it after check of form is valid

<?php
// src/App/Handler/LoginPageHandler.php
class LoginPageHandler implements MiddlewareInterface
{
    // ...
    public function __construct(
        TemplateRendererInterface $template,
        LoginForm                 $loginForm) { /* */ }

    public function process(
        ServerRequestInterface $request,
        RequestHandlerInterface $handler
    ) : ResponseInterface
    {
        // .......
        $error  = '';
        if ($request->getMethod() === 'POST') {
            $this->loginForm->setData($request->getParsedBody());
            if ($this->loginForm->isValid()) {
                $response = $handler->handle($request);
                if ($response->getStatusCode() !== 302) {
                    return new RedirectResponse('/');
                }

                $error = 'Login Failure, please try again';
            }
        }
        // ...

        return new HtmlResponse(
            $this->template->render('app::login-page', [
                'form'  => $this->loginForm,
                'error' => $error,
            ])
        );
    }

We call handle($request) for next Zend\Expressive\Authentication\AuthenticationMiddleware with:

$response = $handler->handle($request);
if ($response->getStatusCode() !== 302) {
    return new RedirectResponse('/');
}

When status code is not 301 302, it authenticated and session filled, we can then redirect to page that need to be authenticated to be access. Failure authentication default behaviour has 301 302 status code which we can set config “redirect” in “authentication” config, on above code, I just want to show it in the login form that the login failure, so I set the $error variable value to “Login Failure, please try again”, so when login failure, it will got the error like the following:

That’s it ;).

How about logout ? We can use clear() method from SessionMiddleware::SESSION_ATTRIBUTE attribute like the following:

use Zend\Expressive\Authentication\UserInterface;
use Zend\Expressive\Session\SessionMiddleware;

class LogoutPageHandler implements RequestHandlerInterface
{
    public function handle(ServerRequestInterface $request) : ResponseInterface
    {
        $session = $request->getAttribute(SessionMiddleware::SESSION_ATTRIBUTE);
        if ($session->has(UserInterface::class)) {
            $session->clear();
        }
        // ...
    }
}

How about authorization part? You can read my next post about create authorization functionality in zend expressive 3

116 Responses

Subscribe to comments with RSS.

  1. mister_bludgeon said, on January 12, 2018 at 10:23 pm

    Thanks for an interesting post. Being not even a novice with Expressive, I’m not yet familiar with the wiring, so I ask: in the snippet that comes after “We call handle($request) for next Zend\Expressive\Authentication\AuthenticationMiddleware with:”, what file would that live in?

    • samsonasik said, on January 13, 2018 at 12:24 am

      the next is a `\Zend\Expressive\Authentication\AuthenticationMiddleware` class which it passed at “login” route at last entry config value, the `\Zend\Expressive\Authentication\ConfigProvider::class` that registered at `config/config.php` handle its service registration.

  2. […] yesterday, I already posted about Authentication part in “Create Login functionality in Expressive 3” post, so, it’s time for authorization part. If you didn’t read that, please read […]

  3. […] you followed my post about authentication and authorization posts with Expressive 3, this time, I write another session related post for […]

  4. […] you already followed my 4 previous expressive posts, all requirements already […]

  5. Juan said, on June 28, 2018 at 2:28 am

    I get the following error: Service with name “Zend\Expressive\Authentication\AuthenticationInterface” could not be created. Reason: The redirect configuration is missing for authentication.

    • samsonasik said, on July 1, 2018 at 2:11 pm

      I retried with run “composer update” to get latest dependencies, and it just works! You probably missing something in the steps, please follow slowly.

    • fancybuddy said, on August 16, 2018 at 9:39 pm

      Of course you have to provide the key ‘redirect’ in the [‘config’][‘authentication’] configuration file as described in Zend\Expressive\Authentication\Session\PhpSessionFactory (where the AuthenticationInterface is mapped in)

      The key is used in case of unauthorizedResponse

  6. caesar_z said, on July 18, 2018 at 9:29 pm

    Hello? Have you had the project code, folder or structure? I have the following error: “Service with name “App\Handler\LoginPageHandler” could not be created. Reason: A plugin by the name “App\Form\LoginForm” was not found in the plugin manager Zend\Form\FormElementManager\FormElementManagerV3Polyfill”

    • samsonasik said, on July 30, 2018 at 2:31 pm

      as in setup described in the post, you need to install `zend-form` and ensure `\Zend\Form\ConfigProvider::class` registered in the ConfigAggregator. By that, the `App\Form\LoginForm` will got auto discover via FormElementManager.

      • James said, on August 3, 2018 at 1:10 am

        Am having the same issue and have read through a couple times. Could you explain this?
        My zend expressive skeloton has src/App/src as the main folder

      • samsonasik said, on August 3, 2018 at 9:46 am

        do you use `zend-servicemanager` and `zend-view` ? If you’re using both components for container and template engine, that should works.

    • Aziz Bouyahyaoui said, on April 13, 2019 at 2:31 pm

      // src/App/Form/LoginForm.php must be // src/App/src/Form/LoginForm.php

      • samsonasik said, on April 14, 2019 at 7:56 am

        that depends on what expressive project structure you have, both can work as far as the composer pointed to the correct directory for `App` namespace.

    • powergamecoder said, on April 16, 2019 at 3:18 am

      I am getting the same error. And I have zend-form and zend-view installed by composer. Even ran composer update but still the error persists. \Zend\Form\ConfigProvider::class is also registered in the config aggregator.
      It seems this problem has to do with https://github.com/zendframework/zend-servicemanager/issues/165.

      Using FormElementManagerV3Polyfill::class instead also does not help.

      Do you have any ideas how to fix this?

  7. icetee said, on November 19, 2018 at 6:00 am

    Missing ‘redirect’ => ‘/login’ in [‘config’][‘authentication’] config.

    • samsonasik said, on December 26, 2018 at 4:33 pm

      Thank you. I’ve updated the post with latest compatible components settings.

  8. powergamecoder said, on April 15, 2019 at 2:55 am

    Thanks. Very informative and professional

  9. powergamecoder said, on April 15, 2019 at 4:25 am

    I’m getting this error:
    Uncaught Zend\ConfigAggregator\InvalidConfigProviderException: Cannot read config from Zend\Expressive\Sesssion\ConfigProvider – class cannot be loaded.

  10. powergamecoder said, on April 15, 2019 at 9:39 pm

    Previous errors gone. Cannot find any difference between my code and yours, but still getting the below error:

    `Too few arguments to function App\Handler\LoginPageHandler::__construct(), 0 passed in /path/to/myproject/vendor/zendframework/zend-servicemanager/src/Factory/InvokableFactory.php on line 30 and exactly 2 expected`

    • samsonasik said, on April 15, 2019 at 10:08 pm

      The error already gave you the hint, you’re using InvokableFactory, while it needs a real factory class to build the LoginPageHandler instance and pass 2 parameters (if you are using based of tutorial). Please follow tutorial slowly, and do effort.

  11. Alex Martins said, on September 2, 2019 at 4:37 am

    I have the following error:

    Unable to resolve service “Zend\Expressive\Template\TemplateRendererInterface” to a factory; are you certain you provided it during configuration?

    Can you help me?

    Estou com o seguinte erro:

    Unable to resolve service “Zend\Expressive\Template\TemplateRendererInterface” to a factory; are you certain you provided it during configuration?

    Podem me ajudar?

    • samsonasik said, on September 2, 2019 at 5:26 pm

      When install zend-expressive-skeleton in the first place with default application type, you can choose “zend-view” when asked for template engine to be used:

        Which template engine do you want to use?
        [1] Plates
        [2] Twig
        [3] zend-view installs zend-servicemanager
        [n] None of the above
        Make your selection or type a composer package name and version (n): 3
      
  12. george said, on September 26, 2019 at 10:03 pm

    Unable to resolve service “Zend\Expressive\Application” to a factory;

    Can you help me?

  13. charles said, on October 7, 2019 at 7:23 pm

    Now I have the following errror: ‘Too few arguments to function App\Handler\HomePageHandler::__construct(), 0 passed in /var/www/fyn.nl/playground/blog/vendor/zendframework/zend-expressive/src/MiddlewareContainer.php on line 64 and exactly 3 expected’

    Can you help?

  14. charles said, on October 7, 2019 at 7:54 pm

    How can I install zend-view template engine?

    • samsonasik said, on October 8, 2019 at 2:50 am

      in very first install of the skeleton, you need to:
      1. choose service container, choose zend servicemanager
      2. choose template engine, choose zend view

      • charles said, on October 11, 2019 at 10:42 pm

        You were right indeed I had not downloaded a template engine. but I have another error: Unable to render template “error::error”;

      • samsonasik said, on October 13, 2019 at 8:53 am

        are you sure you install non-minimal nstallation on the first place ( please choose modular or flat instead )

      • charles said, on October 14, 2019 at 3:06 pm

        are you sure you install non-minimal installation on the first place ( please choose modular or flat instead )

        Reply: Probably i did it wrong but how can I change it?

      • samsonasik said, on October 14, 2019 at 5:41 pm

        you can check structure at https://github.com/samsonasik/expressive3-example-auth-with-prg to compare.

      • charles said, on October 14, 2019 at 6:25 pm

        it looks the same, you may have another idea o fix this?

      • samsonasik said, on October 14, 2019 at 10:34 pm

        do you run php development server with command:

        php -S localhost:8080 -t public

        if no, please try and open with browser http://localhost:8080 . otherwise, i have no idea, the error shows that you don’t have error.phtml file.

  15. Lewis Henderson said, on October 10, 2019 at 2:53 pm

    Hi I have a question about LoginPageHandler. The first LoginPageHandler file I did exactly what you did but at the Authentication process you didn’t type some lines. My question is do I have to type those lines or not.

    • samsonasik said, on October 10, 2019 at 7:55 pm

      Yes, you need to type that, “// …….” lines mean you need to apply after previous code.

  16. Josh said, on October 22, 2019 at 3:24 pm

    ‘Service with name “Zend\Expressive\Authentication\AuthenticationMiddleware” could not be created. Reason: AuthenticationInterface service is missing’

    Can you help please?

    • samsonasik said, on October 23, 2019 at 7:40 am

      please check step on defining `Zend\Expressive\Authentication\AuthenticationInterface::class` in factory in the post at config/autoload/dependencies.global.php.

  17. Dylan Willliams said, on October 27, 2019 at 4:41 am

    When I log in, I get this error: Cannot fetch middleware service “AuthenticationMiddleware”; service not registered, or does not resolve to an autoloadable class name.

    Can you help me?

    • samsonasik said, on October 27, 2019 at 12:04 pm

      please check if ` \Zend\Expressive\Authentication\ConfigProvider::class,` registered in `config/config.php`, follow the post step by step.

      • Dylan Willliams said, on October 27, 2019 at 5:55 pm

        \Zend\Expressive\Authentication\ConfigProvider::class is just in config/config.php. Do you have another idea?

      • samsonasik said, on October 27, 2019 at 6:44 pm

        that’s probably cache issue, please ensure there is no cache files in data/cache directory. Also, run the composer development-enable command. Anyway, I created sample repo at https://github.com/samsonasik/expressive3-example-auth-with-prg

  18. Dylan Willliams said, on October 27, 2019 at 7:17 pm

    There is no cache file in data/cache and i did run the composer development-enable command.
    But it still doesn’t work and I get the same error. I also looked at your github page but that seems almost the same.

    So any other idea?

    • samsonasik said, on October 27, 2019 at 9:45 pm

      Did you try clone and run my repo on github with follow the steps in the readme file ?

  19. Dylan Williams said, on October 28, 2019 at 11:48 pm

    No actually I was doing the tutorial above but this tutorial should actually work right?
    I have still the same error. I can share my github that you can see what I might be doing wrong.

  20. James smith said, on October 29, 2019 at 4:59 am

    How should you do the sql part for mysql (https://localhost/phpmyadmin)? how should you do the sql part for localhost / phpmyadmin? I’m stuck in the sql part, could you explain it for mysql?

    • samsonasik said, on October 29, 2019 at 11:54 am

      well, it seems you need to learn mysql first. hashing using blowfish can utilize php password_hash function, or you can generate first online, eg http://www.passwordtool.hu/blowfish-password-hash-generator

      • James Smith said, on October 30, 2019 at 5:46 pm

        I know the basics of Mysql but your line (NSERT INTO users(username, password) VALUES(‘samsonasik’, crypt(‘123456’, gen_salt(‘bf’)));) can’t on mysql.(phpmyadmin)

        On the internet I only see examples for PHP itself. And I don’t know where to put those queries in send expressive.

      • samsonasik said, on October 30, 2019 at 7:03 pm

        you can get generated the bcrypt value first via php or online first, then apply the value to sql insert:

        INSERT INTO users(username, password) VALUES(‘samsonasik’, ‘the generated bcrypt value for 123456’);

  21. Dylan Williams said, on October 30, 2019 at 8:55 pm

    Service with name “Zend\Expressive\Authentication\UserRepository\PdoDatabase” could not be created. Reason: SQLSTATE[HY000] [1045] Access denied for user ”@’localhost’ (using password: NO)

    Can you help?

  22. James Smith said, on November 4, 2019 at 8:40 pm

    I get these errors:

    A comma or a closing bracket was expected. (near “varying” at position 38)
    Unexpected beginning of statement. (near “255” at position 46)
    Unrecognized statement type. (near “PRIMARY KEY” at position 51).

    When I put this line in sql: CREATE TABLE users(username character varying(255) PRIMARY KEY NOT NULL, password text NOT NULL);

    • samsonasik said, on November 5, 2019 at 1:43 am

      use varchar(255) instead of character varying(255) for mysql.

      • James smith said, on November 5, 2019 at 2:51 am

        wanneer ik inlog met het correcte gebruikersnaam en wachtwoord krijg ik de $error: Login Failure, please try again.

        Do you have any idea?

      • samsonasik said, on November 5, 2019 at 3:11 am

        please do effort, check the error slowly!

  23. Josh said, on November 6, 2019 at 6:31 pm

    I would like to call the PDO, how can I do that?

  24. david09832 said, on November 7, 2019 at 10:09 pm

    Can you make a tutorial about zend-db?

  25. owen89 said, on November 8, 2019 at 4:31 pm

    wrong:
    ‘authentication’ => [
    ‘pdo’ => [
    ‘dsn’ => ‘pgsql:host=localhost;port=5432;dbname=expressive;user=developer;password=xxxxx’,
    ‘table’ => ‘users’,
    ‘field’ => [
    ‘identity’ => ‘username’,
    ‘password’ => ‘password’,
    ],
    It should be this:
    ‘authentication’ => [
    ‘pdo’ => [
    ‘dsn’ => ‘pgsql:host=localhost;dbname=expressive’,
    ‘username’ => ‘developer’,
    ‘password’ => ‘xxxxx’,
    ‘table’ => ‘users’,
    ‘field’ => [
    ‘identity’ => ‘username’,
    ‘password’ => ‘password’,
    ],

    • samsonasik said, on November 8, 2019 at 8:11 pm

      both are correct, you can choose what you like.

      • owen89 said, on November 29, 2019 at 10:38 pm

        I have still the same error, With your code I get this error: (local.php)

        Service with name “Zend\Expressive\Authentication\UserRepository\PdoDatabase” could not be created. Reason: SQLSTATE[HY000] [1045] Access denied for user ”@’localhost’ (using password: NO)

        But with my code I don’t get an error but nothing happens when I click on login button.
        Only what happens is that the error appears.(Login Failure, please try again)
        So any idea to help, let me know please.

      • samsonasik said, on November 30, 2019 at 12:45 am

        compare your database setting and your local.php setting, eg with my config, it is using postgresql with user=developer and password=xxxxx and db name=expressive.

      • owen89 said, on December 2, 2019 at 4:11 pm

        If I put this in my code then I get the output correctly.

        try
        {
        $pdo = new PDO(“mysql:host=$servername;port=3306;dbname=test;”, $username, $password);
        $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        $pdotest = “Connected successfully”;
        }
        catch(PDOException $e)
        {
        $pdotest = “Connection failed: ” . $e->getMessage();
        }

        That is exactly the same line that is in local.php.

      • samsonasik said, on December 2, 2019 at 7:27 pm

        I assume you use empty password for MySQL user=root, you can use the following dns string:

        'dsn'   => 'mysql:host=localhost;port=3306;dbname=expressive;user=root;password=',
        

        In Db, you can insert to users table with dbname=expressive:

        create database expressive;
        
        use expressive;
        
        CREATE TABLE users(username varchar(255) PRIMARY KEY NOT NULL, password text NOT NULL);
        
        insert into users(username, password) values('samsonasik', '$2y$12$eRb1tMuaOwdpk9XVyiCg5eMAOYnQBpF38QX.u.roI1sjMf4MpHQUu');
        

        above, I inserted username=samsonasik, password = bcrypted of 123456 into users table.

      • owen89 said, on December 2, 2019 at 9:09 pm

        I even created a whole new project and it doesn’t work yet. Did you also try it yourself with a new project. If
        If I don’t put the username and password in the dsn then I don’t get an error but I still can’t log in. so I haven’t got anything further. 😦

        Maybe it is more convenient that we do this via e-mail because there are still a number of messages?

      • samsonasik said, on December 2, 2019 at 10:37 pm

        I tried myself on mysql and that works. As developer, please do effort. more effort.

      • owen89 said, on December 2, 2019 at 11:42 pm

        If I don’t hash the password, does it not work anyway?
        I do not hash the password because I want to see if it works, then of course I will.

      • samsonasik said, on December 3, 2019 at 3:28 am

        it uses zend-expressive-authentication middleware that uses password_verify function, you need to pass bcrypted data in the database. see https://github.com/zendframework/zend-expressive-authentication/blob/master/src/UserRepository/PdoDatabase.php#L85

      • owen89 said, on December 3, 2019 at 3:43 pm

        Yes it works thank you. it only works if you hashed the password. But now I have the following problem. When I am logged in I will be redirected to the homepage but if I go to /login again I will have to log in again. Do you know why this line is set to false?

        if ($ session-> has (Userinterface :: class))

      • samsonasik said, on December 3, 2019 at 9:25 pm

        I assume you have missed use statement of:

        use Zend\Expressive\Authentication\UserInterface;
        

        in LoginPageHandler.

        You can debug that session is there with $_SESSION. Please read phpmanual https://www.php.net/manual/en/function.session-start.php and learn about how to debug php app https://stackify.com/php-debugging-guide/ yourself.

      • Lucy Jones said, on December 5, 2019 at 3:27 pm

        I have the same issue, I can log in and the output ($session->has(UserInterface::class) is true.
        But when I am on a other page then is it false.
        It’s weird because I never destroyed the session.

      • samsonasik said, on December 5, 2019 at 11:27 pm

        Login page require `session->has(UserInterface::class)` check to be true, while other pages requre `session->has(UserInterface::class)` to be false as it will redirect to login page when it not login yet, eg:

        in LoginPageHandler:

                if ($session->has(UserInterface::class)) {
                    return new RedirectResponse('/');
                }
        

        in HomePageHandler:

                if (! $session->has(UserInterface::class)) {
                    return new RedirectResponse('/login');
                }
        

        or just add the `AuthenticationMiddleware` before the HomePageHandler like the blog post:

            $app->route('/', [
                \Zend\Expressive\Authentication\AuthenticationMiddleware::class,
                App\Handler\HomePageHandler::class,
            ], ['GET'], 'home');
        

        I updated repository for its example at https://github.com/samsonasik/expressive3-example-auth-with-prg that already uses laminas components. you can follow the readme.

        If the issue persist, it may related with your php ini setting, eg: session save path, etc. you can debug it.

    • owen89 said, on November 14, 2019 at 4:38 pm

      Thank you, I did have an echo somewhere.
      With your code I get this error:

      Service with name “Zend\Expressive\Authentication\UserRepository\PdoDatabase” could not be created. Reason: SQLSTATE[HY000] [1045] Access denied for user ”@’localhost’ (using password: NO)

      But with my code i don’t get an error.

      • samsonasik said, on November 14, 2019 at 10:37 pm

        that’s your db config issue, check your local db config.

  26. owen89 said, on November 11, 2019 at 3:34 pm

    If I use your lines then i get this error: Unable to emit response; headers already sent

    • samsonasik said, on November 12, 2019 at 1:38 am

      it seems you have whitespace in the first line of files or echoed content somewhere in the middleware/handler, check again, slowly

      • owen89 said, on November 12, 2019 at 3:56 pm

        I don’t see anything strange, this is my LoginPageHandler maybe you see something wrong.

        class LoginPageHandler implements MiddlewareInterface
        {
        private $template;
        private $loginForm;

        public function __construct(
        TemplateRendererInterface $template,
        LoginForm $loginForm
        ){
        $this->template = $template;
        $this->loginForm = $loginForm;
        }

        public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
        {
        $session = $request->getAttribute(SessionMiddleware::SESSION_ATTRIBUTE);
        if ($session->has(UserInterface::class)) {
        return new RedirectResponse(‘/blog2/public/’);
        }

        $error = ”;
        if ($request->getMethod() === ‘POST’)
        {

        $this->loginForm->setData($request->getParsedBody());
        if ($this->loginForm->isValid())
        {
        $response = $handler->handle($request);
        // var_dump($response->getStatusCode());
        // die;
        if ($response->getStatusCode() !== 302)
        {
        return new RedirectResponse(‘/blog2/public/’);
        }

        $error = ‘Login Failure, please try again’;
        }
        }

        return new HtmlResponse(
        $this->template->render(‘app::login-page’, [
        ‘form’ => $this->loginForm,
        ‘error’ => $error,
        ])
        );
        }
        }

      • samsonasik said, on November 13, 2019 at 5:20 am

        you can use php-cs-fixer to detect / fix whitespace https://github.com/FriendsOfPHP/PHP-CS-Fixer

      • owen89 said, on November 13, 2019 at 3:57 pm

        but if you look to my code, do you see something that is not right?

      • samsonasik said, on November 13, 2019 at 7:02 pm

        it is hard to check whitespace in blog post comme t, you can check yourself.

  27. Lewis Henderson said, on November 13, 2019 at 5:39 pm

    should it be in this order?

    $app->route(‘/’, [
    \Zend\Expressive\Authentication\AuthenticationMiddleware::class, App\Handler\HomePageHandler::class ], [‘GET’], ‘home’);

    (Now I can’t go to the homepage)

    Because if I do it like this below then I can just go to the homepage:

    $app->route(‘/blog2/public/’, [App\Handler\HomePageHandler::class, AuthenticationMiddleware::class, ], [‘GET’], ‘home’);

  28. Rick said, on November 15, 2019 at 9:06 pm

    zend expressive Method Zend\View\Helper\HeadTitle::__toString() must not throw an exception, caught Zend\I18n\Exception\ExtensionNotLoadedException: Zend\I18n\Translator component requires the intl PHP extension

    Can you help?

    • samsonasik said, on November 16, 2019 at 3:07 am

      based on the error, you need to install intl php extension

  29. Sebastian said, on November 18, 2019 at 5:53 pm

    Hi, I want to send a variable (string) with the return new RedirectResponse function.
    But I don’t know how to do that.

    I have seen at your githubpage you use flash messenger.
    But are there any other ways?

    • samsonasik said, on November 19, 2019 at 7:45 am

      you can bring value to other pages by GET request parameter, eg: ?message=hello and you can get query parameter based on it

  30. Lando said, on November 21, 2019 at 5:26 pm

    So this line of code ( if ($session->has(UserInterface::class)) ) can I compare with a login check right?

  31. Tom said, on November 29, 2019 at 10:51 pm

    How can I check if the username or password is correct / incorrect?

    • samsonasik said, on November 30, 2019 at 12:40 am

      it is already there after form->isValid() check, it utilize AuthenticationMiddleware which check response status code, if response code !=302, the combination of username and password correct.

      • Tom said, on December 2, 2019 at 3:41 pm

        okay but there nothing happens when I enter the correct username and password.
        I checked your tutorial and githubpage and it looks the same.
        Do you know what I can do to fix this?

      • samsonasik said, on December 2, 2019 at 6:46 pm

        ensure that you have bcrypted password in users table, you can check first with password_verify function

      • Tom said, on December 2, 2019 at 6:51 pm

        I did not have my password hashed to be sure.

  32. ericmaddison12 said, on December 5, 2019 at 4:14 pm

    Hi, I have a question about the LoginPageFactory.php. PHPstorm says that FormElementMangager::class an undefined class is. I don’t get any error but I was wondering if it’s okay.

    • samsonasik said, on December 5, 2019 at 9:58 pm

      that’s fine as that is a service name, not a real class name

      • ericmaddison12 said, on December 5, 2019 at 10:18 pm

        thank you, phpstorm would probably mean it well 😀

  33. Josh said, on December 18, 2019 at 7:33 pm

    When I log in this line ( var_dump($response->getStatusCode()) die; ) get this output: int(404).
    Is that supposed to be?


Leave a reply to powergamecoder Cancel reply